From f498c5db13e5db3ae14a504c7e7a40f0d5a7866e Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Wed, 16 Jul 2025 20:03:23 +0530
Subject: [PATCH 01/55] added: removeDefaultOrgEntityTypes to
 readAllSystemEntityTypes

---
 src/services/entityType.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/services/entityType.js b/src/services/entityType.js
index 05c6b58f9..2f4f604a0 100644
--- a/src/services/entityType.js
+++ b/src/services/entityType.js
@@ -98,11 +98,12 @@ module.exports = class EntityHelper {
 			)
 			const defaultOrgId = defaultOrg.id
 
-			const attributes = ['value', 'label', 'id']
+			const attributes = ['value', 'label', 'id', 'organization_id']
 
 			const entities = await entityTypeQueries.findAllEntityTypes([orgId, defaultOrgId], attributes)
+			const prunedEntities = removeDefaultOrgEntityTypes(entities, orgId)
 
-			if (!entities.length) {
+			if (!prunedEntities.length) {
 				return responses.failureResponse({
 					message: 'ENTITY_TYPE_NOT_FOUND',
 					statusCode: httpStatusCode.bad_request,
@@ -112,7 +113,7 @@ module.exports = class EntityHelper {
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,
 				message: 'ENTITY_TYPE_FETCHED_SUCCESSFULLY',
-				result: entities,
+				result: prunedEntities,
 			})
 		} catch (error) {
 			throw error

From 510cde2ea7e7cbf3d7aafac8b0539ae92a7bdf2b Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Wed, 16 Jul 2025 20:06:21 +0530
Subject: [PATCH 02/55] added: foreign keys and join queries to fetch entity
 types along with their entities

---
 src/database/models/entities.js    |  7 +++++--
 src/database/models/entityType.js  |  4 ++--
 src/database/queries/entityType.js | 27 +++++++++++++--------------
 src/services/entityType.js         | 16 +++++++++++++---
 4 files changed, 33 insertions(+), 21 deletions(-)

diff --git a/src/database/models/entities.js b/src/database/models/entities.js
index 3f31a7565..c0991dfef 100644
--- a/src/database/models/entities.js
+++ b/src/database/models/entities.js
@@ -23,14 +23,17 @@ module.exports = (sequelize, DataTypes) => {
 		},
 		{ sequelize, modelName: 'Entity', tableName: 'entities', freezeTableName: true, paranoid: true }
 	)
-	/* 	Entity.associate = (models) => {
+
+	Entity.associate = (models) => {
 		Entity.belongsTo(models.EntityType, {
 			foreignKey: 'entity_type_id',
 			as: 'entity_type',
+			targetKey: 'id',
 			scope: {
 				deleted_at: null, // Only associate with active EntityType records
 			},
 		})
-	} */
+	}
+
 	return Entity
 }
diff --git a/src/database/models/entityType.js b/src/database/models/entityType.js
index 0d5e37337..bc527ea93 100644
--- a/src/database/models/entityType.js
+++ b/src/database/models/entityType.js
@@ -36,7 +36,7 @@ module.exports = (sequelize, DataTypes) => {
 		},
 		{ sequelize, modelName: 'EntityType', tableName: 'entity_types', freezeTableName: true, paranoid: true }
 	)
-	/* 	EntityType.associate = (models) => {
+	EntityType.associate = (models) => {
 		EntityType.hasMany(models.Entity, {
 			foreignKey: 'entity_type_id',
 			as: 'entities',
@@ -44,7 +44,7 @@ module.exports = (sequelize, DataTypes) => {
 				deleted_at: null, // Only associate with active EntityType records
 			},
 		})
-	} */
+	}
 
 	EntityType.addHook('beforeDestroy', async (instance, options) => {
 		try {
diff --git a/src/database/queries/entityType.js b/src/database/queries/entityType.js
index 49163b199..5c792d1da 100644
--- a/src/database/queries/entityType.js
+++ b/src/database/queries/entityType.js
@@ -43,29 +43,28 @@ module.exports = class UserEntityData {
 		try {
 			const entityTypes = await EntityType.findAll({
 				where: filter,
-				raw: true,
-			})
-
-			const entityTypeIds = entityTypes.map((entityType) => entityType.id)
-
-			const entities = await Entity.findAll({
-				where: { entity_type_id: entityTypeIds, status: 'ACTIVE' },
-				raw: true,
-				//attributes: { exclude: ['entity_type_id'] },
+				include: [
+					{
+						model: Entity,
+						as: 'entities',
+						where: { status: filter.status, tenant_code: filter.tenant_code }, // Ensure tenant isolation and citus compatibility
+						required: false, // LEFT JOIN to include entity types with no entities
+					},
+				],
 			})
 
 			const result = entityTypes.map((entityType) => {
-				const matchingEntities = entities.filter((entity) => entity.entity_type_id === entityType.id)
+				const plainEntityType = entityType.get({ plain: true })
 				return {
-					...entityType,
-					entities: matchingEntities,
+					...plainEntityType,
+					entities: plainEntityType.entities || [], // alias is 'entities'
 				}
 			})
 
 			return result
 		} catch (error) {
-			console.error('Error fetching data:', error)
-			throw error
+			console.error('Error fetching entity types and entities:', error)
+			throw new Error(`Failed to fetch data: ${error.message}`)
 		}
 	}
 
diff --git a/src/services/entityType.js b/src/services/entityType.js
index 2f4f604a0..566ed0a26 100644
--- a/src/services/entityType.js
+++ b/src/services/entityType.js
@@ -122,28 +122,38 @@ module.exports = class EntityHelper {
 
 	static async readUserEntityTypes(body, userId, orgId, tenantCode) {
 		try {
-			let defaultOrg = await organizationQueries.findOne(
+			// Fetch default organization ID (consider caching this)
+			const defaultOrg = await organizationQueries.findOne(
 				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantCode },
 				{ attributes: ['id'] }
 			)
-			let defaultOrgId = defaultOrg.id
+			if (!defaultOrg) {
+				throw new Error('Default organization not found')
+			}
+			const defaultOrgId = defaultOrg.id
+
+			// Include tenant_code in filter for consistency with schema
 			const filter = {
 				value: body.value,
 				status: 'ACTIVE',
+				tenant_code: tenantCode, // Ensure tenant isolation
 				organization_id: {
 					[Op.in]: [orgId, defaultOrgId],
 				},
 			}
+
 			const entities = await entityTypeQueries.findUserEntityTypesAndEntities(filter)
 
+			// Deduplicate entity types by value, prioritizing orgId
 			const prunedEntities = removeDefaultOrgEntityTypes(entities, orgId)
+
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,
 				message: 'ENTITY_TYPE_FETCHED_SUCCESSFULLY',
 				result: { entity_types: prunedEntities },
 			})
 		} catch (error) {
-			console.log(error)
+			console.error('Error in readUserEntityTypes:', error)
 			throw error
 		}
 	}

From 17d7c7063500a9106f1cf94adc3de1b667c33d12 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Mon, 28 Jul 2025 13:53:11 +0530
Subject: [PATCH 03/55] fix organization code

---
 src/scripts/correct-org-code/fix-org-code.js | 220 +++++++++++++++++++
 src/validators/v1/organization.js            |   7 +-
 2 files changed, 226 insertions(+), 1 deletion(-)
 create mode 100644 src/scripts/correct-org-code/fix-org-code.js

diff --git a/src/scripts/correct-org-code/fix-org-code.js b/src/scripts/correct-org-code/fix-org-code.js
new file mode 100644
index 000000000..7b4a8a646
--- /dev/null
+++ b/src/scripts/correct-org-code/fix-org-code.js
@@ -0,0 +1,220 @@
+const { Sequelize } = require('sequelize')
+require('dotenv').config({ path: '../../.env' })
+
+// Environment setup
+const nodeEnv = process.env.NODE_ENV || 'development'
+
+let databaseUrl
+
+switch (nodeEnv) {
+	case 'production':
+		databaseUrl = process?.env?.PROD_DATABASE_URL || process.env.DEV_DATABASE_URL
+		break
+	case 'test':
+		databaseUrl = process?.env?.TEST_DATABASE_URL || process.env.DEV_DATABASE_URL
+		break
+	default:
+		databaseUrl = process.env.DEV_DATABASE_URL
+}
+
+console.info('Database selected: ', databaseUrl.split('/').at(-1))
+
+// Initialize Sequelize
+const sequelize = new Sequelize(databaseUrl, {
+	dialect: 'postgres',
+	logging: process.env.NODE_ENV === 'development' ? console.log : false,
+})
+
+;(async () => {
+	let transaction
+	try {
+		// Start a transaction
+		transaction = await sequelize.transaction()
+
+		const DISABLE_FK_QUERY = 'SET CONSTRAINTS ALL DEFERRED' // temporarily remove constraint checks till the transaction is completed
+
+		const ORG_UPDATE_QUERY = `UPDATE organizations SET code = REGEXP_REPLACE(code, '\s+', '', 'g') WHERE code ~ '\s+';`
+		const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\s+';`
+
+		// Test database connection
+		await sequelize.authenticate()
+		console.log('Database connection established successfully.')
+		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		// Execute the query with replacements
+		const updateOrgs = await sequelize.query(ORG_UPDATE_QUERY, {
+			type: Sequelize.QueryTypes.UPDATE,
+			raw: true,
+			transaction,
+		})
+		// Execute the query with replacements
+		const fetchOrgs = await sequelize.query(ORG_FETCH_QUERY, {
+			type: Sequelize.QueryTypes.UPDATE,
+			raw: true,
+			transaction,
+		})
+
+		console.log(fetchOrgs)
+		if (transaction) await transaction.rollback()
+		/*
+        const orgsToRemove = fetchOrgs
+            .filter((orgs) => orgs.code !== process.env.DEFAULT_ORGANISATION_CODE)
+            .map((orgs) => orgs.id)
+        const orgsCodesToRemove = fetchOrgs
+            .map((orgs) => orgs.code)
+            .filter((code) => code !== process.env.DEFAULT_ORGANISATION_CODE)
+        console.info('Organizations to remove: ', fetchOrgs)
+
+        // Fetch system admin and org admin roles
+        const allowedRoles = ['admin', 'org_admin']
+        const ROLES_FETCH_QUERY =
+            'SELECT id FROM user_roles WHERE tenant_code IN (:allowedTenants) AND title IN (:allowedRoles)'
+        const fetchRoles = await sequelize.query(ROLES_FETCH_QUERY, {
+            replacements: { allowedTenants: allowed_tenants, allowedRoles },
+            type: Sequelize.QueryTypes.SELECT,
+            raw: true,
+            transaction
+        })
+
+        const systemRoles = fetchRoles.map((role) => role.id)
+        console.log('System Role ids: ', systemRoles)
+
+        const FETCH_USERID_QUERY = 'SELECT user_id FROM user_organization_roles WHERE role_id IN (:systemRoles)'
+        const fetchUserIds = await sequelize.query(FETCH_USERID_QUERY, {
+            replacements: { orgsCodesToRemove, systemRoles, allowedTenants: allowed_tenants },
+            type: Sequelize.QueryTypes.SELECT,
+            raw: true,
+            transaction
+        })
+
+        const adminRoles = fetchUserIds.map((user) => user.user_id)
+
+        const REFETCH_USERID_QUERY =
+            'SELECT user_id FROM user_organization_roles WHERE user_id NOT IN (:adminRoles) OR organization_code IN (:orgsCodesToRemove)'
+        const reFetchUserIds = await sequelize.query(REFETCH_USERID_QUERY, {
+            replacements: { adminRoles, orgsCodesToRemove },
+            type: Sequelize.QueryTypes.SELECT,
+            raw: true,
+            transaction
+        })
+
+        const userIdsToDelete = reFetchUserIds.map((user) => user.user_id)
+        console.log('User ids to delete: ', userIdsToDelete)
+
+        // Delete user org roles
+        const DELETE_USER_ORG_ROLES_QUERY = 'DELETE FROM user_organization_roles WHERE user_id IN (:userIdsToDelete)'
+        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+        const deleteOrgUserRoles = await sequelize.query(DELETE_USER_ORG_ROLES_QUERY, {
+            replacements: { userIdsToDelete },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Delete user organizations
+        const DELETE_USER_ORGS_QUERY = 'DELETE FROM user_organizations WHERE user_id IN (:userIdsToDelete)'
+        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+        const deleteOrgUsers = await sequelize.query(DELETE_USER_ORGS_QUERY, {
+            replacements: { userIdsToDelete },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Delete users
+        const DELETE_USERS_QUERY = 'DELETE FROM users WHERE id IN (:userIdsToDelete)'
+        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+        const deleteUsers = await sequelize.query(DELETE_USERS_QUERY, {
+            replacements: { userIdsToDelete },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Truncate uploads
+        const TRUNCATE_UPLOADS_QUERY = 'TRUNCATE TABLE file_uploads RESTART IDENTITY'
+        const truncateUploads = await sequelize.query(TRUNCATE_UPLOADS_QUERY, {
+            type: Sequelize.QueryTypes.RAW,
+            raw: true,
+            transaction
+        })
+
+        // Truncate invites
+        const TRUNCATE_INVITES_QUERY = 'TRUNCATE TABLE organization_user_invites RESTART IDENTITY'
+        const truncateInvites = await sequelize.query(TRUNCATE_INVITES_QUERY, {
+            type: Sequelize.QueryTypes.RAW,
+            raw: true,
+            transaction
+        })
+
+        // Delete organization features
+        const DELETE_ORG_FEATURES_QUERY =
+            'DELETE FROM organization_features WHERE organization_code IN (:orgsCodesToRemove)'
+        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+        const deleteOrganizationFeatures = await sequelize.query(DELETE_ORG_FEATURES_QUERY, {
+            replacements: { orgsCodesToRemove },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Delete organization domains
+        const DELETE_ORG_DOMAINS_QUERY =
+            'DELETE FROM organization_email_domains WHERE organization_id IN (:orgsToRemove)'
+        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+        const deleteOrganizationDomains = await sequelize.query(DELETE_ORG_DOMAINS_QUERY, {
+            replacements: { orgsToRemove },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Delete organization role requests
+        const DELETE_ORG_ROLE_REQUEST_QUERY =
+            'DELETE FROM organization_role_requests WHERE organization_id IN (:orgsToRemove)'
+        const deleteOrganizatioRoleRequest = await sequelize.query(DELETE_ORG_ROLE_REQUEST_QUERY, {
+            replacements: { orgsToRemove },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Delete organizations
+        const DELETE_ORGS_QUERY = 'DELETE FROM organizations WHERE id IN (:orgsToRemove)'
+        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+        const deleteOrganizations = await sequelize.query(DELETE_ORGS_QUERY, {
+            replacements: { orgsToRemove },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Delete tenant domains
+        const DELETE_TENANT_DOMAINS_QUERY = 'DELETE FROM tenant_domains WHERE tenant_code NOT IN (:allowed_tenants)'
+        const deleteTenantDomains = await sequelize.query(DELETE_TENANT_DOMAINS_QUERY, {
+            replacements: { allowed_tenants },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Delete tenants
+        const DELETE_TENANTS_QUERY = 'DELETE FROM tenants WHERE code NOT IN (:allowed_tenants)'
+        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+        const deleteTenants = await sequelize.query(DELETE_TENANTS_QUERY, {
+            replacements: { allowed_tenants },
+            type: Sequelize.QueryTypes.DELETE,
+            raw: true,
+            transaction
+        })
+
+        // Commit the transaction
+        await transaction.commit(); */
+		console.log('Transaction committed successfully.')
+	} catch (error) {
+		// Rollback transaction on error
+		if (transaction) await transaction.rollback()
+		console.error(`Error during transaction: ${error}`)
+	} finally {
+		sequelize.close()
+	}
+})()
diff --git a/src/validators/v1/organization.js b/src/validators/v1/organization.js
index fe4beece9..fd80a1708 100644
--- a/src/validators/v1/organization.js
+++ b/src/validators/v1/organization.js
@@ -12,7 +12,12 @@ const common = require('@constants/common')
 module.exports = {
 	create: (req) => {
 		req.body = filterRequestBody(req.body, organization.create)
-		req.checkBody('code').trim().notEmpty().withMessage('code field is empty')
+		req.checkBody('code')
+			.trim()
+			.notEmpty()
+			.withMessage('code field is empty')
+			.matches(/^[a-z0-9]+$/)
+			.withMessage('code is invalid. Only lowercase alphanumeric characters allowed')
 		req.checkBody('tenant_code').trim().notEmpty().withMessage('tenant_code field is empty')
 		req.checkBody('registration_codes')
 			.optional({ checkFalsy: true })

From 42126ecf6ca22007672780ba36fafe4dbdbf974c Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Mon, 28 Jul 2025 18:51:19 +0530
Subject: [PATCH 04/55] final

---
 src/scripts/correct-org-code/fix-org-code.js | 272 ++++++++-----------
 1 file changed, 114 insertions(+), 158 deletions(-)

diff --git a/src/scripts/correct-org-code/fix-org-code.js b/src/scripts/correct-org-code/fix-org-code.js
index 7b4a8a646..497304626 100644
--- a/src/scripts/correct-org-code/fix-org-code.js
+++ b/src/scripts/correct-org-code/fix-org-code.js
@@ -4,6 +4,8 @@ require('dotenv').config({ path: '../../.env' })
 // Environment setup
 const nodeEnv = process.env.NODE_ENV || 'development'
 
+let fk_retainer = []
+let table, fk_name, fkey, refTable, refKey
 let databaseUrl
 
 switch (nodeEnv) {
@@ -33,182 +35,136 @@ const sequelize = new Sequelize(databaseUrl, {
 
 		const DISABLE_FK_QUERY = 'SET CONSTRAINTS ALL DEFERRED' // temporarily remove constraint checks till the transaction is completed
 
-		const ORG_UPDATE_QUERY = `UPDATE organizations SET code = REGEXP_REPLACE(code, '\s+', '', 'g') WHERE code ~ '\s+';`
 		const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\s+';`
+		const disableFK = (table, fk_name) => `ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS ${fk_name};`
+		const enableFK = (table, fk_name, fkey, refTable, refKey) =>
+			`ALTER TABLE ${table} ADD CONSTRAINT ${fk_name} FOREIGN KEY ${fkey} REFERENCES ${refTable} ${refKey} ON UPDATE NO ACTION ON DELETE CASCADE;`
+		const updateQuery = (table, key) =>
+			`UPDATE ${table} SET ${key} = REGEXP_REPLACE(${key}, '\\s+', '', 'g') WHERE ${key} ~ '\\s+';`
 
+		// Execute the query with replacements
+		const fetchOrg = await sequelize.query(ORG_FETCH_QUERY, {
+			type: Sequelize.QueryTypes.SELECT,
+			raw: true,
+			transaction,
+		})
+
+		console.log(fetchOrg)
 		// Test database connection
 		await sequelize.authenticate()
 		console.log('Database connection established successfully.')
+
+		table = 'organization_registration_codes'
+		fk_name = 'fk_organization_code_tenant_code_in_org_reg_code'
+		fkey = '(organization_code, tenant_code)'
+		refTable = 'organizations'
+		refKey = '(code, tenant_code)'
+		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+
+		table = 'user_organizations'
+		fk_name = 'fk_user_organizations_organizations'
+		fkey = '(organization_code, tenant_code)'
+		refTable = 'organizations'
+		refKey = '(code, tenant_code)'
+		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+		table = 'organization_user_invites'
+		fk_name = 'fk_org_user_invites_org_code'
+		fkey = '(organization_code, tenant_code)'
+		refTable = 'organizations'
+		refKey = '(code, tenant_code)'
+		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+		table = 'organization_user_invites'
+		fk_name = 'fk_org_user_invites_organization_id'
+		fkey = '(organization_code, tenant_code)'
+		refTable = 'organizations'
+		refKey = '(code, tenant_code)'
+		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+		table = 'user_organization_roles'
+		fk_name = 'fk_user_org_roles_user_organizations'
+		fkey = '(user_id, organization_code, tenant_code)'
+		refTable = 'user_organizations'
+		refKey = '(user_id, organization_code, tenant_code)'
+		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
 		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
+		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+		let updateTable = 'organizations'
+		let key = 'code'
 		// Execute the query with replacements
-		const updateOrgs = await sequelize.query(ORG_UPDATE_QUERY, {
+		const updateOrgs = await sequelize.query(updateQuery(updateTable, key), {
 			type: Sequelize.QueryTypes.UPDATE,
 			raw: true,
 			transaction,
 		})
+		console.log('------------>>', updateOrgs)
+		updateTable = 'organization_registration_codes'
+		key = 'organization_code'
 		// Execute the query with replacements
-		const fetchOrgs = await sequelize.query(ORG_FETCH_QUERY, {
+		await sequelize.query(updateQuery(updateTable, key), {
+			type: Sequelize.QueryTypes.UPDATE,
+			raw: true,
+			transaction,
+		})
+
+		updateTable = 'organization_user_invites'
+		key = 'organization_code'
+		// Execute the query with replacements
+		await sequelize.query(updateQuery(updateTable, key), {
 			type: Sequelize.QueryTypes.UPDATE,
 			raw: true,
 			transaction,
 		})
 
+		updateTable = 'user_organizations'
+		key = 'organization_code'
+		// Execute the query with replacements
+		await sequelize.query(updateQuery(updateTable, key), {
+			type: Sequelize.QueryTypes.UPDATE,
+			raw: true,
+			transaction,
+		})
+		updateTable = 'user_organization_roles'
+		key = 'organization_code'
+		// Execute the query with replacements
+		await sequelize.query(updateQuery(updateTable, key), {
+			type: Sequelize.QueryTypes.UPDATE,
+			raw: true,
+			transaction,
+		})
+
+		// Execute the query with replacements
+		const fetchOrgs = await sequelize.query(ORG_FETCH_QUERY, {
+			type: Sequelize.QueryTypes.SELECT,
+			raw: true,
+			transaction,
+		})
+
 		console.log(fetchOrgs)
-		if (transaction) await transaction.rollback()
-		/*
-        const orgsToRemove = fetchOrgs
-            .filter((orgs) => orgs.code !== process.env.DEFAULT_ORGANISATION_CODE)
-            .map((orgs) => orgs.id)
-        const orgsCodesToRemove = fetchOrgs
-            .map((orgs) => orgs.code)
-            .filter((code) => code !== process.env.DEFAULT_ORGANISATION_CODE)
-        console.info('Organizations to remove: ', fetchOrgs)
-
-        // Fetch system admin and org admin roles
-        const allowedRoles = ['admin', 'org_admin']
-        const ROLES_FETCH_QUERY =
-            'SELECT id FROM user_roles WHERE tenant_code IN (:allowedTenants) AND title IN (:allowedRoles)'
-        const fetchRoles = await sequelize.query(ROLES_FETCH_QUERY, {
-            replacements: { allowedTenants: allowed_tenants, allowedRoles },
-            type: Sequelize.QueryTypes.SELECT,
-            raw: true,
-            transaction
-        })
-
-        const systemRoles = fetchRoles.map((role) => role.id)
-        console.log('System Role ids: ', systemRoles)
-
-        const FETCH_USERID_QUERY = 'SELECT user_id FROM user_organization_roles WHERE role_id IN (:systemRoles)'
-        const fetchUserIds = await sequelize.query(FETCH_USERID_QUERY, {
-            replacements: { orgsCodesToRemove, systemRoles, allowedTenants: allowed_tenants },
-            type: Sequelize.QueryTypes.SELECT,
-            raw: true,
-            transaction
-        })
-
-        const adminRoles = fetchUserIds.map((user) => user.user_id)
-
-        const REFETCH_USERID_QUERY =
-            'SELECT user_id FROM user_organization_roles WHERE user_id NOT IN (:adminRoles) OR organization_code IN (:orgsCodesToRemove)'
-        const reFetchUserIds = await sequelize.query(REFETCH_USERID_QUERY, {
-            replacements: { adminRoles, orgsCodesToRemove },
-            type: Sequelize.QueryTypes.SELECT,
-            raw: true,
-            transaction
-        })
-
-        const userIdsToDelete = reFetchUserIds.map((user) => user.user_id)
-        console.log('User ids to delete: ', userIdsToDelete)
-
-        // Delete user org roles
-        const DELETE_USER_ORG_ROLES_QUERY = 'DELETE FROM user_organization_roles WHERE user_id IN (:userIdsToDelete)'
-        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-        const deleteOrgUserRoles = await sequelize.query(DELETE_USER_ORG_ROLES_QUERY, {
-            replacements: { userIdsToDelete },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Delete user organizations
-        const DELETE_USER_ORGS_QUERY = 'DELETE FROM user_organizations WHERE user_id IN (:userIdsToDelete)'
-        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-        const deleteOrgUsers = await sequelize.query(DELETE_USER_ORGS_QUERY, {
-            replacements: { userIdsToDelete },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Delete users
-        const DELETE_USERS_QUERY = 'DELETE FROM users WHERE id IN (:userIdsToDelete)'
-        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-        const deleteUsers = await sequelize.query(DELETE_USERS_QUERY, {
-            replacements: { userIdsToDelete },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Truncate uploads
-        const TRUNCATE_UPLOADS_QUERY = 'TRUNCATE TABLE file_uploads RESTART IDENTITY'
-        const truncateUploads = await sequelize.query(TRUNCATE_UPLOADS_QUERY, {
-            type: Sequelize.QueryTypes.RAW,
-            raw: true,
-            transaction
-        })
-
-        // Truncate invites
-        const TRUNCATE_INVITES_QUERY = 'TRUNCATE TABLE organization_user_invites RESTART IDENTITY'
-        const truncateInvites = await sequelize.query(TRUNCATE_INVITES_QUERY, {
-            type: Sequelize.QueryTypes.RAW,
-            raw: true,
-            transaction
-        })
-
-        // Delete organization features
-        const DELETE_ORG_FEATURES_QUERY =
-            'DELETE FROM organization_features WHERE organization_code IN (:orgsCodesToRemove)'
-        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-        const deleteOrganizationFeatures = await sequelize.query(DELETE_ORG_FEATURES_QUERY, {
-            replacements: { orgsCodesToRemove },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Delete organization domains
-        const DELETE_ORG_DOMAINS_QUERY =
-            'DELETE FROM organization_email_domains WHERE organization_id IN (:orgsToRemove)'
-        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-        const deleteOrganizationDomains = await sequelize.query(DELETE_ORG_DOMAINS_QUERY, {
-            replacements: { orgsToRemove },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Delete organization role requests
-        const DELETE_ORG_ROLE_REQUEST_QUERY =
-            'DELETE FROM organization_role_requests WHERE organization_id IN (:orgsToRemove)'
-        const deleteOrganizatioRoleRequest = await sequelize.query(DELETE_ORG_ROLE_REQUEST_QUERY, {
-            replacements: { orgsToRemove },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Delete organizations
-        const DELETE_ORGS_QUERY = 'DELETE FROM organizations WHERE id IN (:orgsToRemove)'
-        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-        const deleteOrganizations = await sequelize.query(DELETE_ORGS_QUERY, {
-            replacements: { orgsToRemove },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Delete tenant domains
-        const DELETE_TENANT_DOMAINS_QUERY = 'DELETE FROM tenant_domains WHERE tenant_code NOT IN (:allowed_tenants)'
-        const deleteTenantDomains = await sequelize.query(DELETE_TENANT_DOMAINS_QUERY, {
-            replacements: { allowed_tenants },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Delete tenants
-        const DELETE_TENANTS_QUERY = 'DELETE FROM tenants WHERE code NOT IN (:allowed_tenants)'
-        await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-        const deleteTenants = await sequelize.query(DELETE_TENANTS_QUERY, {
-            replacements: { allowed_tenants },
-            type: Sequelize.QueryTypes.DELETE,
-            raw: true,
-            transaction
-        })
-
-        // Commit the transaction
-        await transaction.commit(); */
+		let fk_retainerPromise = []
+		for (let i = 0; i < fk_retainer.length; i++) {
+			fk_retainerPromise.push(
+				sequelize.query(fk_retainer[i], {
+					type: Sequelize.QueryTypes.UPDATE,
+					raw: true,
+					transaction,
+				})
+			)
+		}
+
+		await Promise.all(fk_retainerPromise)
+
+		if (transaction) await transaction.commit()
+
 		console.log('Transaction committed successfully.')
 	} catch (error) {
 		// Rollback transaction on error

From d7d1fd592118b20e370b30fbf4a143f6e008781f Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Tue, 29 Jul 2025 12:34:02 +0530
Subject: [PATCH 05/55] Converted file to migration

---
 .../migrations/20250729064710-org-code-fix.js | 168 +++++++++++++++++
 src/scripts/correct-org-code/fix-org-code.js  | 176 ------------------
 2 files changed, 168 insertions(+), 176 deletions(-)
 create mode 100644 src/database/migrations/20250729064710-org-code-fix.js
 delete mode 100644 src/scripts/correct-org-code/fix-org-code.js

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
new file mode 100644
index 000000000..6b6217f28
--- /dev/null
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -0,0 +1,168 @@
+const { Sequelize } = require('sequelize')
+
+module.exports = {
+	async up(queryInterface, Sequelize) {
+		let transaction
+		let fk_retainer = []
+		let table, fk_name, fkey, refTable, refKey
+
+		try {
+			// Start a transaction
+			transaction = await queryInterface.sequelize.transaction()
+
+			const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\s+';`
+			const disableFK = (table, fk_name) => `ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS ${fk_name};`
+			const enableFK = (table, fk_name, fkey, refTable, refKey) =>
+				`ALTER TABLE ${table} ADD CONSTRAINT ${fk_name} FOREIGN KEY ${fkey} REFERENCES ${refTable} ${refKey} ON UPDATE NO ACTION ON DELETE CASCADE;`
+			const updateQuery = (table, key) =>
+				`UPDATE ${table} SET ${key} = REGEXP_REPLACE(${key}, '\\s+', '', 'g') WHERE ${key} ~ '\\s+';`
+
+			// Execute the query to fetch organizations with whitespace
+			const fetchOrg = await queryInterface.sequelize.query(ORG_FETCH_QUERY, {
+				type: Sequelize.QueryTypes.SELECT,
+				raw: true,
+				transaction,
+			})
+			console.log(fetchOrg)
+
+			// Disable foreign key constraints and store enable queries
+			table = 'organization_registration_codes'
+			fk_name = 'fk_organization_code_tenant_code_in_org_reg_code'
+			fkey = '(organization_code, tenant_code)'
+			refTable = 'organizations'
+			refKey = '(code, tenant_code)'
+			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+			await queryInterface.sequelize.query(disableFK(table, fk_name), {
+				type: Sequelize.QueryTypes.RAW,
+				raw: true,
+				transaction,
+			})
+
+			table = 'user_organizations'
+			fk_name = 'fk_user_organizations_organizations'
+			fkey = '(organization_code, tenant_code)'
+			refTable = 'organizations'
+			refKey = '(code, tenant_code)'
+			await queryInterface.sequelize.query(disableFK(table, fk_name), {
+				type: Sequelize.QueryTypes.RAW,
+				raw: true,
+				transaction,
+			})
+			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+			table = 'organization_user_invites'
+			fk_name = 'fk_org_user_invites_org_code'
+			fkey = '(organization_code, tenant_code)'
+			refTable = 'organizations'
+			refKey = '(code, tenant_code)'
+			await queryInterface.sequelize.query(disableFK(table, fk_name), {
+				type: Sequelize.QueryTypes.RAW,
+				raw: true,
+				transaction,
+			})
+			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+			table = 'organization_user_invites'
+			fk_name = 'fk_org_user_invites_organization_id'
+			fkey = '(organization_code, tenant_code)'
+			refTable = 'organizations'
+			refKey = '(code, tenant_code)'
+			await queryInterface.sequelize.query(disableFK(table, fk_name), {
+				type: Sequelize.QueryTypes.RAW,
+				raw: true,
+				transaction,
+			})
+			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+			table = 'user_organization_roles'
+			fk_name = 'fk_user_org_roles_user_organizations'
+			fkey = '(user_id, organization_code, tenant_code)'
+			refTable = 'user_organizations'
+			refKey = '(user_id, organization_code, tenant_code)'
+			await queryInterface.sequelize.query(disableFK(table, fk_name), {
+				type: Sequelize.QueryTypes.RAW,
+				raw: true,
+				transaction,
+			})
+			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+			// Update tables to remove whitespace
+			let updateTable = 'organizations'
+			let key = 'code'
+			const updateOrgs = await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+				type: Sequelize.QueryTypes.UPDATE,
+				raw: true,
+				transaction,
+			})
+
+			updateTable = 'organization_registration_codes'
+			key = 'organization_code'
+			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+				type: Sequelize.QueryTypes.UPDATE,
+				raw: true,
+				transaction,
+			})
+
+			updateTable = 'organization_user_invites'
+			key = 'organization_code'
+			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+				type: Sequelize.QueryTypes.UPDATE,
+				raw: true,
+				transaction,
+			})
+
+			updateTable = 'user_organizations'
+			key = 'organization_code'
+			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+				type: Sequelize.QueryTypes.UPDATE,
+				raw: true,
+				transaction,
+			})
+
+			updateTable = 'user_organization_roles'
+			key = 'organization_code'
+			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+				type: Sequelize.QueryTypes.UPDATE,
+				raw: true,
+				transaction,
+			})
+
+			// Verify the update
+			const fetchOrgs = await queryInterface.sequelize.query(ORG_FETCH_QUERY, {
+				type: Sequelize.QueryTypes.SELECT,
+				raw: true,
+				transaction,
+			})
+			console.log(fetchOrgs)
+
+			// Re-enable foreign key constraints
+			let fk_retainerPromise = []
+			for (let i = 0; i < fk_retainer.length; i++) {
+				fk_retainerPromise.push(
+					queryInterface.sequelize.query(fk_retainer[i], {
+						type: Sequelize.QueryTypes.RAW,
+						raw: true,
+						transaction,
+					})
+				)
+			}
+
+			await Promise.all(fk_retainerPromise)
+
+			// Commit the transaction
+			await transaction.commit()
+			console.log('Transaction committed successfully.')
+		} catch (error) {
+			// Rollback transaction on error
+			if (transaction) await transaction.rollback()
+			console.error(`Error during transaction: ${error}`)
+			throw error
+		}
+	},
+
+	async down(queryInterface, Sequelize) {
+		console.warn(
+			'Down migration not implemented: Cannot reliably restore original whitespace in organization codes.'
+		)
+	},
+}
diff --git a/src/scripts/correct-org-code/fix-org-code.js b/src/scripts/correct-org-code/fix-org-code.js
deleted file mode 100644
index 497304626..000000000
--- a/src/scripts/correct-org-code/fix-org-code.js
+++ /dev/null
@@ -1,176 +0,0 @@
-const { Sequelize } = require('sequelize')
-require('dotenv').config({ path: '../../.env' })
-
-// Environment setup
-const nodeEnv = process.env.NODE_ENV || 'development'
-
-let fk_retainer = []
-let table, fk_name, fkey, refTable, refKey
-let databaseUrl
-
-switch (nodeEnv) {
-	case 'production':
-		databaseUrl = process?.env?.PROD_DATABASE_URL || process.env.DEV_DATABASE_URL
-		break
-	case 'test':
-		databaseUrl = process?.env?.TEST_DATABASE_URL || process.env.DEV_DATABASE_URL
-		break
-	default:
-		databaseUrl = process.env.DEV_DATABASE_URL
-}
-
-console.info('Database selected: ', databaseUrl.split('/').at(-1))
-
-// Initialize Sequelize
-const sequelize = new Sequelize(databaseUrl, {
-	dialect: 'postgres',
-	logging: process.env.NODE_ENV === 'development' ? console.log : false,
-})
-
-;(async () => {
-	let transaction
-	try {
-		// Start a transaction
-		transaction = await sequelize.transaction()
-
-		const DISABLE_FK_QUERY = 'SET CONSTRAINTS ALL DEFERRED' // temporarily remove constraint checks till the transaction is completed
-
-		const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\s+';`
-		const disableFK = (table, fk_name) => `ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS ${fk_name};`
-		const enableFK = (table, fk_name, fkey, refTable, refKey) =>
-			`ALTER TABLE ${table} ADD CONSTRAINT ${fk_name} FOREIGN KEY ${fkey} REFERENCES ${refTable} ${refKey} ON UPDATE NO ACTION ON DELETE CASCADE;`
-		const updateQuery = (table, key) =>
-			`UPDATE ${table} SET ${key} = REGEXP_REPLACE(${key}, '\\s+', '', 'g') WHERE ${key} ~ '\\s+';`
-
-		// Execute the query with replacements
-		const fetchOrg = await sequelize.query(ORG_FETCH_QUERY, {
-			type: Sequelize.QueryTypes.SELECT,
-			raw: true,
-			transaction,
-		})
-
-		console.log(fetchOrg)
-		// Test database connection
-		await sequelize.authenticate()
-		console.log('Database connection established successfully.')
-
-		table = 'organization_registration_codes'
-		fk_name = 'fk_organization_code_tenant_code_in_org_reg_code'
-		fkey = '(organization_code, tenant_code)'
-		refTable = 'organizations'
-		refKey = '(code, tenant_code)'
-		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-
-		table = 'user_organizations'
-		fk_name = 'fk_user_organizations_organizations'
-		fkey = '(organization_code, tenant_code)'
-		refTable = 'organizations'
-		refKey = '(code, tenant_code)'
-		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-		table = 'organization_user_invites'
-		fk_name = 'fk_org_user_invites_org_code'
-		fkey = '(organization_code, tenant_code)'
-		refTable = 'organizations'
-		refKey = '(code, tenant_code)'
-		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-		table = 'organization_user_invites'
-		fk_name = 'fk_org_user_invites_organization_id'
-		fkey = '(organization_code, tenant_code)'
-		refTable = 'organizations'
-		refKey = '(code, tenant_code)'
-		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-		table = 'user_organization_roles'
-		fk_name = 'fk_user_org_roles_user_organizations'
-		fkey = '(user_id, organization_code, tenant_code)'
-		refTable = 'user_organizations'
-		refKey = '(user_id, organization_code, tenant_code)'
-		await sequelize.query(disableFK(table, fk_name), { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		await sequelize.query(DISABLE_FK_QUERY, { type: Sequelize.QueryTypes.RAW, raw: true, transaction })
-		fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-		let updateTable = 'organizations'
-		let key = 'code'
-		// Execute the query with replacements
-		const updateOrgs = await sequelize.query(updateQuery(updateTable, key), {
-			type: Sequelize.QueryTypes.UPDATE,
-			raw: true,
-			transaction,
-		})
-		console.log('------------>>', updateOrgs)
-		updateTable = 'organization_registration_codes'
-		key = 'organization_code'
-		// Execute the query with replacements
-		await sequelize.query(updateQuery(updateTable, key), {
-			type: Sequelize.QueryTypes.UPDATE,
-			raw: true,
-			transaction,
-		})
-
-		updateTable = 'organization_user_invites'
-		key = 'organization_code'
-		// Execute the query with replacements
-		await sequelize.query(updateQuery(updateTable, key), {
-			type: Sequelize.QueryTypes.UPDATE,
-			raw: true,
-			transaction,
-		})
-
-		updateTable = 'user_organizations'
-		key = 'organization_code'
-		// Execute the query with replacements
-		await sequelize.query(updateQuery(updateTable, key), {
-			type: Sequelize.QueryTypes.UPDATE,
-			raw: true,
-			transaction,
-		})
-		updateTable = 'user_organization_roles'
-		key = 'organization_code'
-		// Execute the query with replacements
-		await sequelize.query(updateQuery(updateTable, key), {
-			type: Sequelize.QueryTypes.UPDATE,
-			raw: true,
-			transaction,
-		})
-
-		// Execute the query with replacements
-		const fetchOrgs = await sequelize.query(ORG_FETCH_QUERY, {
-			type: Sequelize.QueryTypes.SELECT,
-			raw: true,
-			transaction,
-		})
-
-		console.log(fetchOrgs)
-		let fk_retainerPromise = []
-		for (let i = 0; i < fk_retainer.length; i++) {
-			fk_retainerPromise.push(
-				sequelize.query(fk_retainer[i], {
-					type: Sequelize.QueryTypes.UPDATE,
-					raw: true,
-					transaction,
-				})
-			)
-		}
-
-		await Promise.all(fk_retainerPromise)
-
-		if (transaction) await transaction.commit()
-
-		console.log('Transaction committed successfully.')
-	} catch (error) {
-		// Rollback transaction on error
-		if (transaction) await transaction.rollback()
-		console.error(`Error during transaction: ${error}`)
-	} finally {
-		sequelize.close()
-	}
-})()

From 2b3a44d43584871377ace754d04ebf28cd6dc32d Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Tue, 29 Jul 2025 12:42:04 +0530
Subject: [PATCH 06/55] minor fixes

---
 .../migrations/20250729064710-org-code-fix.js | 257 +++++++++---------
 1 file changed, 129 insertions(+), 128 deletions(-)

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
index 6b6217f28..72a5a3908 100644
--- a/src/database/migrations/20250729064710-org-code-fix.js
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -10,7 +10,7 @@ module.exports = {
 			// Start a transaction
 			transaction = await queryInterface.sequelize.transaction()
 
-			const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\s+';`
+			const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\\s+';`
 			const disableFK = (table, fk_name) => `ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS ${fk_name};`
 			const enableFK = (table, fk_name, fkey, refTable, refKey) =>
 				`ALTER TABLE ${table} ADD CONSTRAINT ${fk_name} FOREIGN KEY ${fkey} REFERENCES ${refTable} ${refKey} ON UPDATE NO ACTION ON DELETE CASCADE;`
@@ -23,135 +23,136 @@ module.exports = {
 				raw: true,
 				transaction,
 			})
-			console.log(fetchOrg)
-
-			// Disable foreign key constraints and store enable queries
-			table = 'organization_registration_codes'
-			fk_name = 'fk_organization_code_tenant_code_in_org_reg_code'
-			fkey = '(organization_code, tenant_code)'
-			refTable = 'organizations'
-			refKey = '(code, tenant_code)'
-			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-			await queryInterface.sequelize.query(disableFK(table, fk_name), {
-				type: Sequelize.QueryTypes.RAW,
-				raw: true,
-				transaction,
-			})
-
-			table = 'user_organizations'
-			fk_name = 'fk_user_organizations_organizations'
-			fkey = '(organization_code, tenant_code)'
-			refTable = 'organizations'
-			refKey = '(code, tenant_code)'
-			await queryInterface.sequelize.query(disableFK(table, fk_name), {
-				type: Sequelize.QueryTypes.RAW,
-				raw: true,
-				transaction,
-			})
-			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-			table = 'organization_user_invites'
-			fk_name = 'fk_org_user_invites_org_code'
-			fkey = '(organization_code, tenant_code)'
-			refTable = 'organizations'
-			refKey = '(code, tenant_code)'
-			await queryInterface.sequelize.query(disableFK(table, fk_name), {
-				type: Sequelize.QueryTypes.RAW,
-				raw: true,
-				transaction,
-			})
-			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-			table = 'organization_user_invites'
-			fk_name = 'fk_org_user_invites_organization_id'
-			fkey = '(organization_code, tenant_code)'
-			refTable = 'organizations'
-			refKey = '(code, tenant_code)'
-			await queryInterface.sequelize.query(disableFK(table, fk_name), {
-				type: Sequelize.QueryTypes.RAW,
-				raw: true,
-				transaction,
-			})
-			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-			table = 'user_organization_roles'
-			fk_name = 'fk_user_org_roles_user_organizations'
-			fkey = '(user_id, organization_code, tenant_code)'
-			refTable = 'user_organizations'
-			refKey = '(user_id, organization_code, tenant_code)'
-			await queryInterface.sequelize.query(disableFK(table, fk_name), {
-				type: Sequelize.QueryTypes.RAW,
-				raw: true,
-				transaction,
-			})
-			fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
-			// Update tables to remove whitespace
-			let updateTable = 'organizations'
-			let key = 'code'
-			const updateOrgs = await queryInterface.sequelize.query(updateQuery(updateTable, key), {
-				type: Sequelize.QueryTypes.UPDATE,
-				raw: true,
-				transaction,
-			})
-
-			updateTable = 'organization_registration_codes'
-			key = 'organization_code'
-			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
-				type: Sequelize.QueryTypes.UPDATE,
-				raw: true,
-				transaction,
-			})
-
-			updateTable = 'organization_user_invites'
-			key = 'organization_code'
-			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
-				type: Sequelize.QueryTypes.UPDATE,
-				raw: true,
-				transaction,
-			})
-
-			updateTable = 'user_organizations'
-			key = 'organization_code'
-			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
-				type: Sequelize.QueryTypes.UPDATE,
-				raw: true,
-				transaction,
-			})
 
-			updateTable = 'user_organization_roles'
-			key = 'organization_code'
-			await queryInterface.sequelize.query(updateQuery(updateTable, key), {
-				type: Sequelize.QueryTypes.UPDATE,
-				raw: true,
-				transaction,
-			})
-
-			// Verify the update
-			const fetchOrgs = await queryInterface.sequelize.query(ORG_FETCH_QUERY, {
-				type: Sequelize.QueryTypes.SELECT,
-				raw: true,
-				transaction,
-			})
-			console.log(fetchOrgs)
-
-			// Re-enable foreign key constraints
-			let fk_retainerPromise = []
-			for (let i = 0; i < fk_retainer.length; i++) {
-				fk_retainerPromise.push(
-					queryInterface.sequelize.query(fk_retainer[i], {
-						type: Sequelize.QueryTypes.RAW,
-						raw: true,
-						transaction,
-					})
-				)
+			if (fetchOrg.length > 0) {
+				// Disable foreign key constraints and store enable queries
+				table = 'organization_registration_codes'
+				fk_name = 'fk_organization_code_tenant_code_in_org_reg_code'
+				fkey = '(organization_code, tenant_code)'
+				refTable = 'organizations'
+				refKey = '(code, tenant_code)'
+				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+				await queryInterface.sequelize.query(disableFK(table, fk_name), {
+					type: Sequelize.QueryTypes.RAW,
+					raw: true,
+					transaction,
+				})
+
+				table = 'user_organizations'
+				fk_name = 'fk_user_organizations_organizations'
+				fkey = '(organization_code, tenant_code)'
+				refTable = 'organizations'
+				refKey = '(code, tenant_code)'
+				await queryInterface.sequelize.query(disableFK(table, fk_name), {
+					type: Sequelize.QueryTypes.RAW,
+					raw: true,
+					transaction,
+				})
+				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+				table = 'organization_user_invites'
+				fk_name = 'fk_org_user_invites_org_code'
+				fkey = '(organization_code, tenant_code)'
+				refTable = 'organizations'
+				refKey = '(code, tenant_code)'
+				await queryInterface.sequelize.query(disableFK(table, fk_name), {
+					type: Sequelize.QueryTypes.RAW,
+					raw: true,
+					transaction,
+				})
+				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+				table = 'organization_user_invites'
+				fk_name = 'fk_org_user_invites_organization_id'
+				fkey = '(organization_code, tenant_code)'
+				refTable = 'organizations'
+				refKey = '(code, tenant_code)'
+				await queryInterface.sequelize.query(disableFK(table, fk_name), {
+					type: Sequelize.QueryTypes.RAW,
+					raw: true,
+					transaction,
+				})
+				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+				table = 'user_organization_roles'
+				fk_name = 'fk_user_org_roles_user_organizations'
+				fkey = '(user_id, organization_code, tenant_code)'
+				refTable = 'user_organizations'
+				refKey = '(user_id, organization_code, tenant_code)'
+				await queryInterface.sequelize.query(disableFK(table, fk_name), {
+					type: Sequelize.QueryTypes.RAW,
+					raw: true,
+					transaction,
+				})
+				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
+				// Update tables to remove whitespace
+				let updateTable = 'organizations'
+				let key = 'code'
+				const updateOrgs = await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+					type: Sequelize.QueryTypes.UPDATE,
+					raw: true,
+					transaction,
+				})
+
+				updateTable = 'organization_registration_codes'
+				key = 'organization_code'
+				await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+					type: Sequelize.QueryTypes.UPDATE,
+					raw: true,
+					transaction,
+				})
+
+				updateTable = 'organization_user_invites'
+				key = 'organization_code'
+				await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+					type: Sequelize.QueryTypes.UPDATE,
+					raw: true,
+					transaction,
+				})
+
+				updateTable = 'user_organizations'
+				key = 'organization_code'
+				await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+					type: Sequelize.QueryTypes.UPDATE,
+					raw: true,
+					transaction,
+				})
+
+				updateTable = 'user_organization_roles'
+				key = 'organization_code'
+				await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+					type: Sequelize.QueryTypes.UPDATE,
+					raw: true,
+					transaction,
+				})
+
+				// Verify the update
+				const fetchOrgs = await queryInterface.sequelize.query(ORG_FETCH_QUERY, {
+					type: Sequelize.QueryTypes.SELECT,
+					raw: true,
+					transaction,
+				})
+				console.log(fetchOrgs)
+
+				// Re-enable foreign key constraints
+				let fk_retainerPromise = []
+				for (let i = 0; i < fk_retainer.length; i++) {
+					fk_retainerPromise.push(
+						queryInterface.sequelize.query(fk_retainer[i], {
+							type: Sequelize.QueryTypes.RAW,
+							raw: true,
+							transaction,
+						})
+					)
+				}
+
+				await Promise.all(fk_retainerPromise)
+
+				// Commit the transaction
+				await transaction.commit()
+				console.log('Transaction committed successfully.')
 			}
-
-			await Promise.all(fk_retainerPromise)
-
-			// Commit the transaction
-			await transaction.commit()
-			console.log('Transaction committed successfully.')
 		} catch (error) {
 			// Rollback transaction on error
 			if (transaction) await transaction.rollback()

From 9d1494e83adbafb646c67347126c1bfc3f197558 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Tue, 29 Jul 2025 17:49:54 +0530
Subject: [PATCH 07/55] review comments addressed

---
 src/database/migrations/20250729064710-org-code-fix.js | 4 ++--
 src/routes/index.js                                    | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
index 72a5a3908..a1b623b38 100644
--- a/src/database/migrations/20250729064710-org-code-fix.js
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -10,12 +10,12 @@ module.exports = {
 			// Start a transaction
 			transaction = await queryInterface.sequelize.transaction()
 
-			const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\\s+';`
+			const ORG_FETCH_QUERY = `SELECT id, name, code FROM organizations WHERE code ~ '\\s+' OR code ~ '[A-Z]';`
 			const disableFK = (table, fk_name) => `ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS ${fk_name};`
 			const enableFK = (table, fk_name, fkey, refTable, refKey) =>
 				`ALTER TABLE ${table} ADD CONSTRAINT ${fk_name} FOREIGN KEY ${fkey} REFERENCES ${refTable} ${refKey} ON UPDATE NO ACTION ON DELETE CASCADE;`
 			const updateQuery = (table, key) =>
-				`UPDATE ${table} SET ${key} = REGEXP_REPLACE(${key}, '\\s+', '', 'g') WHERE ${key} ~ '\\s+';`
+				`UPDATE ${table} SET ${key} = LOWER(REGEXP_REPLACE(${key}, '\\s+', '_', 'g')) WHERE ${key} ~ '[A-Z|\\s+]';`
 
 			// Execute the query to fetch organizations with whitespace
 			const fetchOrg = await queryInterface.sequelize.query(ORG_FETCH_QUERY, {
diff --git a/src/routes/index.js b/src/routes/index.js
index ab0e329f5..552a20b55 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -70,7 +70,7 @@ module.exports = (app) => {
 		const version = (req.params.version.match(/^v\d+$/) || [])[0] // Match version like v1, v2, etc.
 		const controllerName = (req.params.controller.match(/^[a-zA-Z0-9_-]+$/) || [])[0] // Allow only alphanumeric characters, underscore, and hyphen
 		const file = req.params.file ? (req.params.file.match(/^[a-zA-Z0-9_-]+$/) || [])[0] : null // Same validation as controller, or null if file is not provided
-		const method = (req.params.method.match(/^[a-zA-Z0-9]+$/) || [])[0] // Allow only alphanumeric characters
+		const method = (req.params.method.match(/^[a-zA-Z0-9_-]+$/) || [])[0] // Allow only alphanumeric characters
 		try {
 			if (!version || !controllerName || !method || (req.params.file && !file)) {
 				// Invalid input, return an error response

From 0317a0bda2d6648f29121319519d049d71436ab2 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Tue, 29 Jul 2025 17:54:55 +0530
Subject: [PATCH 08/55] review comments addressed

---
 src/database/migrations/20250729064710-org-code-fix.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
index a1b623b38..008365c83 100644
--- a/src/database/migrations/20250729064710-org-code-fix.js
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -72,7 +72,6 @@ module.exports = {
 					raw: true,
 					transaction,
 				})
-				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
 
 				table = 'user_organization_roles'
 				fk_name = 'fk_user_org_roles_user_organizations'

From 57084411b6fa2095aa5ff56118c5407f407aa72f Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Tue, 29 Jul 2025 17:59:15 +0530
Subject: [PATCH 09/55] review comments addressed

---
 .../migrations/20250729064710-org-code-fix.js | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
index 008365c83..29ee00427 100644
--- a/src/database/migrations/20250729064710-org-code-fix.js
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -85,6 +85,18 @@ module.exports = {
 				})
 				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
 
+				table = 'organization_features'
+				fk_name = 'fk_org_features_organization'
+				fkey = '(organization_code, tenant_code)'
+				refTable = 'organizations'
+				refKey = '(code, tenant_code)'
+				await queryInterface.sequelize.query(disableFK(table, fk_name), {
+					type: Sequelize.QueryTypes.RAW,
+					raw: true,
+					transaction,
+				})
+				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
+
 				// Update tables to remove whitespace
 				let updateTable = 'organizations'
 				let key = 'code'
@@ -125,6 +137,13 @@ module.exports = {
 					raw: true,
 					transaction,
 				})
+				updateTable = 'organization_features'
+				key = 'organization_code'
+				await queryInterface.sequelize.query(updateQuery(updateTable, key), {
+					type: Sequelize.QueryTypes.UPDATE,
+					raw: true,
+					transaction,
+				})
 
 				// Verify the update
 				const fetchOrgs = await queryInterface.sequelize.query(ORG_FETCH_QUERY, {

From c29c5af948158e7ef1ffe48eaa469a17f297a1da Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Wed, 30 Jul 2025 00:16:25 +0530
Subject: [PATCH 10/55] updated: entity type and entities flows

---
 src/controllers/v1/entity-type.js             |  12 +-
 src/controllers/v1/entity.js                  |  15 +-
 src/controllers/v1/user.js                    |   4 +-
 ...ation-code-to-entities-and-entity-types.js | 188 ++++++++++++++++++
 ...0723183753-add-entity-types-constraints.js |  75 +++++++
 src/database/models/entities.js               |   4 +
 src/database/models/entityType.js             |   8 +-
 src/database/queries/entities.js              |  31 ++-
 src/database/queries/entityType.js            |  15 +-
 src/services/account.js                       |  53 ++---
 src/services/entities.js                      | 106 ++++------
 src/services/entityType.js                    |  53 ++---
 src/services/tenant.js                        |  19 +-
 src/services/user.js                          |  53 ++---
 src/validators/v1/entity-type.js              |   3 +-
 src/validators/v1/entity.js                   |  25 ++-
 16 files changed, 454 insertions(+), 210 deletions(-)
 create mode 100644 src/database/migrations/20250717071710-add-organization-code-to-entities-and-entity-types.js
 create mode 100644 src/database/migrations/20250723183753-add-entity-types-constraints.js

diff --git a/src/controllers/v1/entity-type.js b/src/controllers/v1/entity-type.js
index 91e9493bc..7cfc5866e 100644
--- a/src/controllers/v1/entity-type.js
+++ b/src/controllers/v1/entity-type.js
@@ -15,6 +15,7 @@ module.exports = class Entity {
 			return await entityTypeService.create(
 				req.body,
 				req.decodedToken.id,
+				req.decodedToken.organization_code,
 				req.decodedToken.organization_id,
 				req.decodedToken.tenant_code
 			)
@@ -37,7 +38,7 @@ module.exports = class Entity {
 				req.body,
 				req.params.id,
 				req.decodedToken.id,
-				req.decodedToken.organization_id,
+				req.decodedToken.organization_code,
 				req.decodedToken.tenant_code
 			)
 		} catch (error) {
@@ -58,13 +59,14 @@ module.exports = class Entity {
 			if (req.body.value) {
 				return await entityTypeService.readUserEntityTypes(
 					req.body,
-					req.decodedToken.id,
-					req.decodedToken.organization_id,
-					req.decodedToken.tenant_code
+					req.decodedToken.organization_code,
+					req.decodedToken.tenant_code,
+					req.decodedToken.organization_id
 				)
 			}
 			return await entityTypeService.readAllSystemEntityTypes(
 				req.decodedToken.organization_id,
+				req.decodedToken.organization_code,
 				req.decodedToken.tenant_code
 			)
 		} catch (error) {
@@ -82,7 +84,7 @@ module.exports = class Entity {
 
 	async delete(req) {
 		try {
-			return await entityTypeService.delete(req.params.id, req.decodedToken.organization_id)
+			return await entityTypeService.delete(req.params.id, req.decodedToken.organization_code)
 		} catch (error) {
 			return error
 		}
diff --git a/src/controllers/v1/entity.js b/src/controllers/v1/entity.js
index c2a135754..133fb0a77 100644
--- a/src/controllers/v1/entity.js
+++ b/src/controllers/v1/entity.js
@@ -22,7 +22,8 @@ module.exports = class Entity {
 			const createdEntity = await entityService.create(
 				req.body,
 				req.decodedToken.id,
-				req.decodedToken.tenant_code
+				req.decodedToken.tenant_code,
+				req.decodedToken.organization_code
 			)
 			return createdEntity
 		} catch (error) {
@@ -44,7 +45,8 @@ module.exports = class Entity {
 				req.body,
 				req.params.id,
 				req.decodedToken.id,
-				req.decodedToken.roles
+				req.decodedToken.organization_code,
+				req.decodedToken.tenant_code
 			)
 			return updatedEntity
 		} catch (error) {
@@ -63,9 +65,8 @@ module.exports = class Entity {
 	async read(req) {
 		try {
 			if (req.query.id || req.query.value) {
-				return await entityService.read(req.query, req.decodedToken.id)
+				return await entityService.read(req.query, req.decodedToken.tenant_code)
 			}
-			return await entityService.readAll(req.query, null)
 		} catch (error) {
 			return error
 		}
@@ -81,7 +82,11 @@ module.exports = class Entity {
 
 	async delete(req) {
 		try {
-			const updatedEntity = await entityService.delete(req.params.id, req.decodedToken.id)
+			const updatedEntity = await entityService.delete(
+				req.params.id,
+				req.decodedToken.organization_code,
+				req.decodedToken.tenant_code
+			)
 			return updatedEntity
 		} catch (error) {
 			return error
diff --git a/src/controllers/v1/user.js b/src/controllers/v1/user.js
index bb67a990b..160a9911b 100644
--- a/src/controllers/v1/user.js
+++ b/src/controllers/v1/user.js
@@ -24,7 +24,7 @@ module.exports = class User {
 			const updatedUser = await userService.update(
 				params,
 				req.decodedToken.id,
-				req.decodedToken.organization_id,
+				req.decodedToken.organization_code,
 				req.decodedToken.tenant_code
 			)
 			return updatedUser
@@ -118,7 +118,7 @@ module.exports = class User {
 			const updateUsersLanguagePreference = await userService.setLanguagePreference(
 				params,
 				req.decodedToken.id,
-				req.decodedToken.organization_id,
+				req.decodedToken.organization_code,
 				req.decodedToken.tenant_code
 			)
 			return updateUsersLanguagePreference
diff --git a/src/database/migrations/20250717071710-add-organization-code-to-entities-and-entity-types.js b/src/database/migrations/20250717071710-add-organization-code-to-entities-and-entity-types.js
new file mode 100644
index 000000000..f21cd7c22
--- /dev/null
+++ b/src/database/migrations/20250717071710-add-organization-code-to-entities-and-entity-types.js
@@ -0,0 +1,188 @@
+'use strict'
+
+module.exports = {
+	up: async (queryInterface, Sequelize) => {
+		const transaction = await queryInterface.sequelize.transaction()
+		try {
+			// 1. Add organization_code column to entities table
+			await queryInterface.addColumn(
+				'entities',
+				'organization_code',
+				{
+					type: Sequelize.STRING,
+					allowNull: true, // temporarily allow null for data backfill
+				},
+				{ transaction }
+			)
+
+			// 2. Add organization_code column to entity_types table
+			await queryInterface.addColumn(
+				'entity_types',
+				'organization_code',
+				{
+					type: Sequelize.STRING,
+					allowNull: true, // temporarily allow null for data backfill
+				},
+				{ transaction }
+			)
+			// 3. Backfill organization_code in entity_types from organization_id
+			// Assuming there's an organizations table with id and code columns
+			await queryInterface.sequelize.query(
+				`
+				UPDATE entity_types et
+				SET organization_code = o.code
+				FROM organizations o
+				WHERE et.organization_id = o.id
+				AND et.tenant_code = o.tenant_code
+				`,
+				{ transaction }
+			)
+
+			// 4. Backfill organization_code in entities from entity_types
+			await queryInterface.sequelize.query(
+				`
+				UPDATE entities e
+				SET organization_code = et.organization_code
+				FROM entity_types et
+				WHERE e.entity_type_id = et.id
+				AND e.tenant_code = et.tenant_code
+				`,
+				{ transaction }
+			)
+			// 5. Verify backfill - check if any records still have null organization_code
+			const [entitiesNullCount] = await queryInterface.sequelize.query(
+				`SELECT COUNT(*) as count FROM entities WHERE organization_code IS NULL`,
+				{ transaction }
+			)
+
+			const [entityTypesNullCount] = await queryInterface.sequelize.query(
+				`SELECT COUNT(*) as count FROM entity_types WHERE organization_code IS NULL`,
+				{ transaction }
+			)
+
+			if (entitiesNullCount[0].count > 0) {
+				throw new Error(
+					`Backfill failed: ${entitiesNullCount[0].count} entities still have null organization_code`
+				)
+			}
+
+			if (entityTypesNullCount[0].count > 0) {
+				throw new Error(
+					`Backfill failed: ${entityTypesNullCount[0].count} entity_types still have null organization_code`
+				)
+			}
+
+			console.log('✅ Backfill verification passed - all records have organization_code')
+
+			// 6. Set organization_code to NOT NULL in both tables
+			await queryInterface.changeColumn(
+				'entities',
+				'organization_code',
+				{
+					type: Sequelize.STRING,
+					allowNull: false,
+				},
+				{ transaction }
+			)
+
+			await queryInterface.changeColumn(
+				'entity_types',
+				'organization_code',
+				{
+					type: Sequelize.STRING,
+					allowNull: false,
+				},
+				{ transaction }
+			)
+
+			// 7. Remove organization_id from entity_types table
+			//await queryInterface.removeColumn('entity_types', 'organization_id', { transaction })
+
+			// 8. Remove old unique constraint on entities
+			await queryInterface.removeIndex('entities', 'unique_entities_value', {
+				transaction,
+			})
+
+			// 9. Add new unique constraint on entities: value + entity_type_id + organization_code +tenant_code
+			await queryInterface.sequelize.query(
+				`
+  CREATE UNIQUE INDEX unique_entities_value_type_organization_tenant
+  ON "entities" ("value", "entity_type_id", "organization_code", "tenant_code")
+  WHERE deleted_at IS NULL;
+  `,
+				{ transaction }
+			)
+
+			await transaction.commit()
+			console.log('✅ Migration completed successfully')
+		} catch (err) {
+			await transaction.rollback()
+			console.error('❌ Migration failed:', err)
+			throw err
+		}
+	},
+
+	down: async (queryInterface, Sequelize) => {
+		const transaction = await queryInterface.sequelize.transaction()
+		try {
+			// 2. Remove new unique constraint on entities
+			await queryInterface.removeConstraint('entities', 'unique_entities_value_type_organization_tenant', {
+				transaction,
+			})
+
+			// 3. Restore old unique constraint on entities (assuming it was only on value)
+			await queryInterface.addConstraint('entities', {
+				fields: ['value'],
+				type: 'unique',
+				name: 'unique_entities_value',
+				transaction,
+			})
+
+			// 4. Add back organization_id column to entity_types
+			await queryInterface.addColumn(
+				'entity_types',
+				'organization_id',
+				{
+					type: Sequelize.INTEGER,
+					allowNull: true, // temporarily allow null for data restoration
+				},
+				{ transaction }
+			)
+
+			// 5. Restore organization_id in entity_types from organization_code
+			// Assuming there's an organizations table with id and code columns
+			await queryInterface.sequelize.query(
+				`
+				UPDATE entity_types et
+				SET organization_id = o.id
+				FROM organizations o
+				WHERE et.organization_code = o.code
+				AND et.tenant_code = o.tenant_code
+				`,
+				{ transaction }
+			)
+
+			// 6. Set organization_id to NOT NULL in entity_types
+			await queryInterface.changeColumn(
+				'entity_types',
+				'organization_id',
+				{
+					type: Sequelize.INTEGER,
+					allowNull: false,
+				},
+				{ transaction }
+			)
+
+			// 7. Remove organization_code columns
+			await queryInterface.removeColumn('entities', 'organization_code', { transaction })
+			await queryInterface.removeColumn('entity_types', 'organization_code', { transaction })
+
+			await transaction.commit()
+			console.log('✅ Migration rollback completed successfully')
+		} catch (err) {
+			await transaction.rollback()
+			console.error('❌ Migration rollback failed:', err.message)
+			throw err
+		}
+	},
+}
diff --git a/src/database/migrations/20250723183753-add-entity-types-constraints.js b/src/database/migrations/20250723183753-add-entity-types-constraints.js
new file mode 100644
index 000000000..1ac6179e8
--- /dev/null
+++ b/src/database/migrations/20250723183753-add-entity-types-constraints.js
@@ -0,0 +1,75 @@
+'use strict'
+
+module.exports = {
+	up: async (queryInterface, Sequelize) => {
+		const transaction = await queryInterface.sequelize.transaction()
+		try {
+			// Drop existing UNIQUE constraint
+			await queryInterface.sequelize.query(
+				`
+        ALTER TABLE entities
+        DROP CONSTRAINT IF EXISTS unique_entities_value_type_tenant;
+      `,
+				{ transaction }
+			)
+
+			// Add partial unique index
+			await queryInterface.sequelize.query(
+				`
+        CREATE UNIQUE INDEX unique_entities_value_type_tenant
+        ON entities (value, entity_type_id, tenant_code)
+        WHERE deleted_at IS NULL;
+      `,
+				{ transaction }
+			)
+
+			// Add unique constraint on entity_types
+			await queryInterface.addConstraint('entity_types', {
+				fields: ['id', 'organization_code', 'tenant_code'],
+				type: 'unique',
+				name: 'unique_entity_types_id_org_tenant',
+				transaction,
+			})
+
+			// Add foreign key constraint from entities
+			await queryInterface.addConstraint('entities', {
+				fields: ['entity_type_id', 'organization_code', 'tenant_code'],
+				type: 'foreign key',
+				name: 'fk_entities_to_entity_types_composite',
+				references: {
+					table: 'entity_types',
+					fields: ['id', 'organization_code', 'tenant_code'],
+				},
+				onDelete: 'CASCADE',
+				onUpdate: 'NO ACTION',
+				transaction,
+			})
+
+			await transaction.commit()
+		} catch (error) {
+			await transaction.rollback()
+			throw error
+		}
+	},
+
+	down: async (queryInterface, Sequelize) => {
+		const transaction = await queryInterface.sequelize.transaction()
+		try {
+			// Remove foreign key constraint
+			await queryInterface.removeConstraint('entities', 'fk_entities_to_entity_types_composite', { transaction })
+
+			// Remove unique constraint from entity_types
+			await queryInterface.removeConstraint('entity_types', 'unique_entity_types_id_org_tenant', { transaction })
+
+			// Remove partial unique index from entities
+			await queryInterface.sequelize.query(`DROP INDEX IF EXISTS unique_entities_value_type_tenant;`, {
+				transaction,
+			})
+
+			await transaction.commit()
+		} catch (error) {
+			await transaction.rollback()
+			throw error
+		}
+	},
+}
diff --git a/src/database/models/entities.js b/src/database/models/entities.js
index c0991dfef..8c13b4731 100644
--- a/src/database/models/entities.js
+++ b/src/database/models/entities.js
@@ -18,6 +18,10 @@ module.exports = (sequelize, DataTypes) => {
 				type: DataTypes.STRING,
 				allowNull: false,
 			},
+			organization_code: {
+				type: DataTypes.STRING,
+				allowNull: false,
+			},
 			created_by: { type: DataTypes.INTEGER, allowNull: true },
 			updated_by: { type: DataTypes.INTEGER, allowNull: true },
 		},
diff --git a/src/database/models/entityType.js b/src/database/models/entityType.js
index bc527ea93..427825d7d 100644
--- a/src/database/models/entityType.js
+++ b/src/database/models/entityType.js
@@ -16,7 +16,11 @@ module.exports = (sequelize, DataTypes) => {
 			updated_by: { type: DataTypes.INTEGER, allowNull: true },
 			allow_filtering: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
 			data_type: { type: DataTypes.STRING, allowNull: false, defaultValue: 'STRING' },
-			organization_id: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, primaryKey: true },
+			organization_id: { type: DataTypes.INTEGER, allowNull: false },
+			organization_code: {
+				type: DataTypes.STRING,
+				allowNull: false,
+			},
 			parent_id: { type: DataTypes.INTEGER, allowNull: true },
 			allow_custom_entities: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
 			has_entities: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
@@ -31,7 +35,7 @@ module.exports = (sequelize, DataTypes) => {
 			tenant_code: {
 				type: DataTypes.STRING,
 				allowNull: false,
-				defaultValue: '',
+				primaryKey: true,
 			},
 		},
 		{ sequelize, modelName: 'EntityType', tableName: 'entity_types', freezeTableName: true, paranoid: true }
diff --git a/src/database/queries/entities.js b/src/database/queries/entities.js
index fc8831dc1..59371a91a 100644
--- a/src/database/queries/entities.js
+++ b/src/database/queries/entities.js
@@ -1,5 +1,6 @@
 const Entity = require('../models/index').Entity
 const { Op } = require('sequelize')
+const EntityType = require('../models/index').EntityType
 
 module.exports = class UserEntityData {
 	static async createEntity(data) {
@@ -22,12 +23,13 @@ module.exports = class UserEntityData {
 		}
 	}
 
-	static async updateOneEntity(id, update, userId, options = {}) {
+	static async updateOneEntity(id, update, organizationCode, tenantCode, options = {}) {
 		try {
 			return await Entity.update(update, {
 				where: {
 					id: id,
-					created_by: userId,
+					tenant_code: tenantCode,
+					organization_code: organizationCode,
 				},
 				...options,
 			})
@@ -36,12 +38,13 @@ module.exports = class UserEntityData {
 		}
 	}
 
-	static async deleteOneEntityType(id, userId) {
+	static async deleteOneEntity(id, organizationCode, tenantCode) {
 		try {
 			return await Entity.destroy({
 				where: {
 					id: id,
-					created_by: userId,
+					organization_code: organizationCode,
+					tenant_code: tenantCode,
 				},
 			})
 		} catch (error) {
@@ -87,4 +90,24 @@ module.exports = class UserEntityData {
 			throw error
 		}
 	}
+
+	static async findEntityWithOrgCheck(id, tenantCode, organizationID) {
+		return Entity.findOne({
+			where: {
+				id,
+				tenant_code: tenantCode,
+			},
+			include: [
+				{
+					model: EntityType,
+					as: 'entity_type',
+					where: {
+						tenant_code: tenantCode,
+						organization_id: organizationID,
+					},
+					required: true,
+				},
+			],
+		})
+	}
 }
diff --git a/src/database/queries/entityType.js b/src/database/queries/entityType.js
index 5c792d1da..2476588e6 100644
--- a/src/database/queries/entityType.js
+++ b/src/database/queries/entityType.js
@@ -1,8 +1,5 @@
 const EntityType = require('../models/index').EntityType
 const Entity = require('../models/index').Entity
-const { Op } = require('sequelize')
-const Sequelize = require('../models/index').sequelize
-
 module.exports = class UserEntityData {
 	static async createEntityType(data) {
 		try {
@@ -24,11 +21,11 @@ module.exports = class UserEntityData {
 		}
 	}
 
-	static async findAllEntityTypes(orgId, attributes, filter = {}) {
+	static async findAllEntityTypes(organizationCode, attributes, filter = {}) {
 		try {
 			const entityData = await EntityType.findAll({
 				where: {
-					organization_id: orgId,
+					organization_code: organizationCode,
 					...filter,
 				},
 				attributes,
@@ -94,12 +91,12 @@ module.exports = class UserEntityData {
 		}
 	} */
 
-	static async updateOneEntityType(id, organizationId, tenantCode, update, options = {}) {
+	static async updateOneEntityType(id, organizationCode, tenantCode, update, options = {}) {
 		try {
 			return await EntityType.update(update, {
 				where: {
 					id: id,
-					organization_id: organizationId,
+					organization_code: organizationCode,
 					tenant_code: tenantCode,
 				},
 				...options,
@@ -109,12 +106,12 @@ module.exports = class UserEntityData {
 		}
 	}
 
-	static async deleteOneEntityType(id, orgId) {
+	static async deleteOneEntityType(id, organizationCode) {
 		try {
 			return await EntityType.destroy({
 				where: {
 					id: id,
-					organization_id: orgId,
+					organization_code: organizationCode,
 				},
 				individualHooks: true,
 			})
diff --git a/src/services/account.js b/src/services/account.js
index 0f29cd4b9..20b8bd981 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -346,29 +346,17 @@ module.exports = class AccountHelper {
 
 			delete bodyData.role
 
-			const [defaultOrg, userOrgDetails, modelName] = await Promise.all([
-				organizationQueries.findOne(
-					{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantDetail.code },
-					{ attributes: ['id'] }
-				),
-				organizationQueries.findOne(
-					{ code: organizationCode, tenant_code: tenantDetail.code },
-					{ attributes: ['id'] }
-				),
-				userQueries.getModelName(),
-			])
+			const modelName = await userQueries.getModelName()
+
+			const defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
 
-			if (!defaultOrg || !userOrgDetails) {
+			if (!defaultOrganizationCode || !organizationCode) {
 				throw new Error('Default or user organization not found.')
 			}
-
-			const defaultOrgId = defaultOrg.id
-			userOrgId = userOrgDetails.id
-
 			const [validationData, userModel] = await Promise.all([
 				entityTypeQueries.findUserEntityTypesAndEntities({
 					status: 'ACTIVE',
-					organization_id: { [Op.in]: [userOrgId, defaultOrgId] },
+					organization_code: { [Op.in]: [defaultOrganizationCode, organizationCode] },
 					model_names: { [Op.contains]: [modelName] },
 					tenant_code: tenantDetail.code,
 				}),
@@ -735,7 +723,7 @@ module.exports = class AccountHelper {
 			}
 
 			// Verify password
-			const isPasswordCorrect = bcryptJs.compareSync(bodyData.password, user.password)
+			const isPasswordCorrect = bcryptJs.compare(bodyData.password, user.password)
 			if (!isPasswordCorrect) {
 				return responses.failureResponse({
 					message: 'IDENTIFIER_OR_PASSWORD_INVALID',
@@ -789,24 +777,22 @@ module.exports = class AccountHelper {
 
 			delete user.password
 
-			// Fetch default organization and validation data
-			let defaultOrg = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantDetail.code },
-				{ attributes: ['id'] }
-			)
-			let defaultOrgId = defaultOrg.id
 			const modelName = await userQueries.getModelName()
 
+			const orgCodes = user.organizations?.map((org) => org.code).filter(Boolean) || []
+			orgCodes.push(process.env.DEFAULT_ORGANISATION_CODE)
+
 			let validationData = await entityTypeQueries.findUserEntityTypesAndEntities({
 				status: 'ACTIVE',
-				organization_id: {
-					[Op.in]: [user.organization_id, defaultOrgId],
+				organization_code: {
+					[Op.in]: orgCodes,
 				},
 				tenant_code: tenantDetail.code,
 				model_names: { [Op.contains]: [modelName] },
 			})
 
-			const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organization_id)
+			const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organizations?.[0]?.id)
+
 			user = await utils.processDbResponse(user, prunedEntities)
 
 			if (user && user.image) {
@@ -1481,23 +1467,20 @@ module.exports = class AccountHelper {
 			delete user.otpInfo
 
 			// Fetch default organization and validation data
-			let defaultOrg = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantDetail.code },
-				{ attributes: ['id'] }
-			)
-			let defaultOrgId = defaultOrg.id
+			const userOrgCodes = user.organizations?.map((org) => org.code) || []
+			const defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
 			const modelName = await userQueries.getModelName()
 
 			let validationData = await entityTypeQueries.findUserEntityTypesAndEntities({
 				status: 'ACTIVE',
-				organization_id: {
-					[Op.in]: [user.organization_id, defaultOrgId],
+				organization_code: {
+					[Op.in]: [...userOrgCodes, defaultOrganizationCode],
 				},
 				model_names: { [Op.contains]: [modelName] },
 				tenant_code: tenantDetail.code,
 			})
 
-			const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organization_id)
+			const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organizations[0].id)
 			user = await utils.processDbResponse(user, prunedEntities)
 
 			// Handle user image
diff --git a/src/services/entities.js b/src/services/entities.js
index 77eaf7598..7e684f943 100644
--- a/src/services/entities.js
+++ b/src/services/entities.js
@@ -1,10 +1,8 @@
 // Dependencies
 const httpStatusCode = require('@generics/http-status')
-const common = require('@constants/common')
-
-const entityTypeQueries = require('@database/queries/entities')
+const entityQueries = require('@database/queries/entities')
+const entityTypeQueries = require('@database/queries/entityType')
 const { UniqueConstraintError, ForeignKeyConstraintError } = require('sequelize')
-const { Op } = require('sequelize')
 const responses = require('@helpers/responses')
 
 module.exports = class EntityHelper {
@@ -17,12 +15,15 @@ module.exports = class EntityHelper {
 	 * @returns {JSON} - Entity created response.
 	 */
 
-	static async create(bodyData, id, tenantCode) {
-		bodyData.created_by = id
-		bodyData.updated_by = id
+	static async create(bodyData, userId, tenantCode, organizationCode) {
+		bodyData.created_by = userId
+		bodyData.updated_by = userId
 		bodyData.tenant_code = tenantCode
+		bodyData.organization_code = organizationCode
+
 		try {
-			const entity = await entityTypeQueries.createEntity(bodyData)
+			const entity = await entityQueries.createEntity(bodyData)
+
 			return responses.successResponse({
 				statusCode: httpStatusCode.created,
 				message: 'ENTITY_CREATED_SUCCESSFULLY',
@@ -36,6 +37,7 @@ module.exports = class EntityHelper {
 					responseCode: 'CLIENT_ERROR',
 				})
 			}
+
 			if (error instanceof ForeignKeyConstraintError) {
 				return responses.failureResponse({
 					message: 'ENTITY_TYPE_NOT_FOUND',
@@ -43,6 +45,7 @@ module.exports = class EntityHelper {
 					responseCode: 'CLIENT_ERROR',
 				})
 			}
+
 			throw error
 		}
 	}
@@ -57,13 +60,19 @@ module.exports = class EntityHelper {
 	 * @returns {JSON} - Entity updated response.
 	 */
 
-	static async update(bodyData, id, loggedInUserId) {
+	static async update(bodyData, id, loggedInUserId, organizationCode, tenantCode) {
 		bodyData.updated_by = loggedInUserId
 		try {
-			const [updateCount, updatedEntity] = await entityTypeQueries.updateOneEntity(id, bodyData, loggedInUserId, {
-				returning: true,
-				raw: true,
-			})
+			const [updateCount, updatedEntity] = await entityQueries.updateOneEntity(
+				id,
+				bodyData,
+				organizationCode,
+				tenantCode,
+				{
+					returning: true,
+					raw: true,
+				}
+			)
 
 			if (updateCount === 0) {
 				return responses.failureResponse({
@@ -97,71 +106,24 @@ module.exports = class EntityHelper {
 	 * @returns {JSON} - Entity read response.
 	 */
 
-	static async read(query, userId) {
+	static async read(query, tenantCode) {
 		try {
 			let filter
 			if (query.id) {
 				filter = {
-					[Op.or]: [
-						{
-							id: query.id,
-							created_by: '0',
-							status: 'ACTIVE',
-						},
-						{ id: query.id, created_by: userId, status: 'ACTIVE' },
-					],
-				}
-			} else {
-				filter = {
-					[Op.or]: [
-						{
-							value: query.value,
-							created_by: '0',
-							status: 'ACTIVE',
-						},
-						{ value: query.value, created_by: userId, status: 'ACTIVE' },
-					],
-				}
-			}
-			const entities = await entityTypeQueries.findAllEntities(filter)
-
-			if (!entities.length) {
-				return responses.failureResponse({
-					message: 'ENTITY_NOT_FOUND',
-					statusCode: httpStatusCode.bad_request,
-					responseCode: 'CLIENT_ERROR',
-				})
-			}
-			return responses.successResponse({
-				statusCode: httpStatusCode.ok,
-				message: 'ENTITY_FETCHED_SUCCESSFULLY',
-				result: entities,
-			})
-		} catch (error) {
-			throw error
-		}
-	}
-
-	static async readAll(query, userId) {
-		try {
-			let filter
-			if (query.read_user_entity == true) {
-				filter = {
-					[Op.or]: [
-						{
-							created_by: '0',
-						},
-						{
-							created_by: userId,
-						},
-					],
+					id: query.id,
+					status: 'ACTIVE',
+					tenant_code: tenantCode,
 				}
 			} else {
 				filter = {
-					created_by: '0',
+					value: query.value,
+					entity_type_id: query.entity_type_id,
+					tenant_code: tenantCode,
+					status: 'ACTIVE',
 				}
 			}
-			const entities = await entityTypeQueries.findAllEntities(filter)
+			const entities = await entityQueries.findAllEntities(filter)
 
 			if (!entities.length) {
 				return responses.failureResponse({
@@ -188,9 +150,9 @@ module.exports = class EntityHelper {
 	 * @returns {JSON} - Entity deleted response.
 	 */
 
-	static async delete(id, userId) {
+	static async delete(id, organizationCode, tenantCode) {
 		try {
-			const deleteCount = await entityTypeQueries.deleteOneEntityType(id, userId)
+			const deleteCount = await entityQueries.deleteOneEntity(id, organizationCode, tenantCode)
 			if (deleteCount === '0') {
 				return responses.failureResponse({
 					message: 'ENTITY_NOT_FOUND',
@@ -228,7 +190,7 @@ module.exports = class EntityHelper {
 			}
 
 			const attributes = ['id', 'entity_type_id', 'value', 'label', 'status', 'type', 'created_by', 'created_at']
-			const entities = await entityTypeQueries.getAllEntities(filter, attributes, pageNo, pageSize, searchText)
+			const entities = await entityQueries.getAllEntities(filter, attributes, pageNo, pageSize, searchText)
 
 			if (entities.rows == 0 || entities.count == 0) {
 				return responses.failureResponse({
diff --git a/src/services/entityType.js b/src/services/entityType.js
index 566ed0a26..5aa88079d 100644
--- a/src/services/entityType.js
+++ b/src/services/entityType.js
@@ -17,10 +17,11 @@ module.exports = class EntityHelper {
 	 * @returns {JSON} - Created entity type response.
 	 */
 
-	static async create(bodyData, id, orgId, tenantCode) {
+	static async create(bodyData, id, organizationCode, organizationId, tenantCode) {
 		bodyData.created_by = id
 		bodyData.updated_by = id
-		bodyData.organization_id = orgId
+		bodyData.organization_code = organizationCode
+		bodyData.organization_id = organizationId
 		bodyData.tenant_code = tenantCode
 		try {
 			const entityType = await entityTypeQueries.createEntityType(bodyData)
@@ -51,12 +52,12 @@ module.exports = class EntityHelper {
 	 * @returns {JSON} - Updated Entity Type.
 	 */
 
-	static async update(bodyData, id, loggedInUserId, orgId, tenantCode) {
-		;(bodyData.updated_by = loggedInUserId), (bodyData.organization_id = orgId)
+	static async update(bodyData, id, loggedInUserId, organizationCode, tenantCode) {
+		;(bodyData.updated_by = loggedInUserId), (bodyData.organization_code = organizationCode)
 		try {
 			const [updateCount, updatedEntityType] = await entityTypeQueries.updateOneEntityType(
 				id,
-				orgId,
+				organizationCode,
 				tenantCode,
 				bodyData,
 				{
@@ -90,18 +91,18 @@ module.exports = class EntityHelper {
 		}
 	}
 
-	static async readAllSystemEntityTypes(orgId, tenantCode) {
+	static async readAllSystemEntityTypes(organizationId, organizationCode, tenantCode) {
 		try {
-			const defaultOrg = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantCode },
-				{ attributes: ['id'] }
-			)
-			const defaultOrgId = defaultOrg.id
-
-			const attributes = ['value', 'label', 'id', 'organization_id']
+			const attributes = ['value', 'label', 'id', 'organization_code']
 
-			const entities = await entityTypeQueries.findAllEntityTypes([orgId, defaultOrgId], attributes)
-			const prunedEntities = removeDefaultOrgEntityTypes(entities, orgId)
+			const entities = await entityTypeQueries.findAllEntityTypes(
+				[organizationCode, process.env.DEFAULT_ORGANISATION_CODE],
+				attributes,
+				{
+					tenant_code: tenantCode,
+				}
+			)
+			const prunedEntities = removeDefaultOrgEntityTypes(entities, organizationId)
 
 			if (!prunedEntities.length) {
 				return responses.failureResponse({
@@ -120,32 +121,22 @@ module.exports = class EntityHelper {
 		}
 	}
 
-	static async readUserEntityTypes(body, userId, orgId, tenantCode) {
+	static async readUserEntityTypes(body, organizationCode, tenantCode, organizationId = '') {
 		try {
-			// Fetch default organization ID (consider caching this)
-			const defaultOrg = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantCode },
-				{ attributes: ['id'] }
-			)
-			if (!defaultOrg) {
-				throw new Error('Default organization not found')
-			}
-			const defaultOrgId = defaultOrg.id
-
 			// Include tenant_code in filter for consistency with schema
 			const filter = {
 				value: body.value,
 				status: 'ACTIVE',
 				tenant_code: tenantCode, // Ensure tenant isolation
-				organization_id: {
-					[Op.in]: [orgId, defaultOrgId],
+				organization_code: {
+					[Op.in]: [process.env.DEFAULT_ORGANISATION_CODE, organizationCode],
 				},
 			}
 
 			const entities = await entityTypeQueries.findUserEntityTypesAndEntities(filter)
 
 			// Deduplicate entity types by value, prioritizing orgId
-			const prunedEntities = removeDefaultOrgEntityTypes(entities, orgId)
+			const prunedEntities = removeDefaultOrgEntityTypes(entities, organizationId)
 
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,
@@ -165,9 +156,9 @@ module.exports = class EntityHelper {
 	 * @returns {JSON} - Entity deleted response.
 	 */
 
-	static async delete(id, orgId) {
+	static async delete(id, organizationCode) {
 		try {
-			const deleteCount = await entityTypeQueries.deleteOneEntityType(id, orgId)
+			const deleteCount = await entityTypeQueries.deleteOneEntityType(id, organizationCode)
 			if (deleteCount === 0) {
 				return responses.failureResponse({
 					message: 'ENTITY_TYPE_NOT_FOUND',
diff --git a/src/services/tenant.js b/src/services/tenant.js
index 88e7e80ac..bda2e3d9a 100644
--- a/src/services/tenant.js
+++ b/src/services/tenant.js
@@ -142,24 +142,23 @@ module.exports = class tenantHelper {
 				}
 
 				const defaultOrgId = defaultOrgCreateResponse.result.id
+				const defaultOrganizationCode = defaultOrgCreateResponse.result.code
 				// add rollback code for org
 				rollbackStack.push(async () => {
 					await organisationQueries.hardDelete(defaultOrgId)
 				})
 
 				// ******* adding default entities and entity types to Default Org CODE BEGINS HERE *******
-				let allDefaultEntityTypeValues = await entityTypeQueries.findAllEntityTypes(
-					process.env.DEFAULT_ORG_ID,
-					['value']
-				)
+				let allDefaultEntityTypeValues = await entityTypeQueries.findAllEntityTypes(defaultOrganizationCode, [
+					'value',
+				])
 				allDefaultEntityTypeValues = allDefaultEntityTypeValues.map((entityType) => entityType.value)
 				if (allDefaultEntityTypeValues.length > 0) {
 					const entityTypeResponse = await entityTypeService.readUserEntityTypes(
 						{
 							value: allDefaultEntityTypeValues,
 						},
-						userId,
-						process.env.DEFAULT_ORG_ID,
+						defaultOrganizationCode,
 						process.env.DEFAULT_TENANT_CODE
 					)
 
@@ -187,6 +186,7 @@ module.exports = class tenantHelper {
 									external_entity_type: entityType?.external_entity_type || false,
 								},
 								userId,
+								defaultOrganizationCode,
 								defaultOrgId,
 								tenantCreateResponse.code
 							)
@@ -218,7 +218,12 @@ module.exports = class tenantHelper {
 
 						// Step 2: Create promises for each entity
 						const entitiesCreationPromise = filteredEntities.map((entity) => {
-							return EntityHelper.create(entity, userId, tenantCreateResponse.code)
+							return EntityHelper.create(
+								entity,
+								userId,
+								tenantCreateResponse.code,
+								defaultOrganizationCode
+							)
 						})
 
 						const createdEntities = await Promise.all(entitiesCreationPromise)
diff --git a/src/services/user.js b/src/services/user.js
index aabb87ee3..e942cc9f9 100644
--- a/src/services/user.js
+++ b/src/services/user.js
@@ -35,7 +35,7 @@ module.exports = class UserHelper {
 	 * @param {string} searchText - search text.
 	 * @returns {JSON} - update user response
 	 */
-	static async update(bodyData, id, orgId, tenantCode) {
+	static async update(bodyData, id, orgCode, tenantCode) {
 		try {
 			if (bodyData.hasOwnProperty('email')) {
 				return responses.failureResponse({
@@ -58,16 +58,12 @@ module.exports = class UserHelper {
 				})
 			}
 
-			let defaultOrg = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantCode },
-				{ attributes: ['id'] }
-			)
-			let defaultOrgId = defaultOrg.id
+			let defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
 
 			const filter = {
 				status: 'ACTIVE',
-				organization_id: {
-					[Op.in]: [orgId, defaultOrgId],
+				organization_code: {
+					[Op.in]: [orgCode, defaultOrganizationCode],
 				},
 				tenant_code: tenantCode,
 				model_names: { [Op.contains]: [await userQueries.getModelName()] },
@@ -348,22 +344,19 @@ module.exports = class UserHelper {
 
 				//user.user_roles = roles
 
-				let defaultOrg = await organizationQueries.findOne(
-					{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantCode },
-					{ attributes: ['id'] }
-				)
-				let defaultOrgId = defaultOrg.id
-				let userOrg = user.organizations[0].id
+				let defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
+
+				let userOrg = user.organizations[0].code
 
 				let validationData = await entityTypeQueries.findUserEntityTypesAndEntities({
 					status: 'ACTIVE',
-					organization_id: {
-						[Op.in]: [userOrg, defaultOrgId],
+					organization_code: {
+						[Op.in]: [userOrg, defaultOrganizationCode],
 					},
 					tenant_code: tenantCode,
 					model_names: { [Op.contains]: [await userQueries.getModelName()] },
 				})
-				const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organization_id)
+				const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organizations[0].id)
 				const permissionsByModule = await this.getPermissions(user.organizations[0].roles)
 				user.permissions = permissionsByModule
 
@@ -456,22 +449,19 @@ module.exports = class UserHelper {
 			if (user.image) {
 				user.image = await utils.getDownloadableUrl(user.image)
 			}
-			let defaultOrg = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantCode },
-				{ attributes: ['id'] }
-			)
-			let defaultOrgId = defaultOrg.id
-			let userOrg = user.organizations[0].id
+			let defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
+
+			let userOrganizationCode = user.organizations[0].code
 
 			let validationData = await entityTypeQueries.findUserEntityTypesAndEntities({
 				status: 'ACTIVE',
-				organization_id: {
-					[Op.in]: [userOrg, defaultOrgId],
+				organization_code: {
+					[Op.in]: [userOrganizationCode, defaultOrganizationCode],
 				},
 				tenant_code: tenantCode,
 				model_names: { [Op.contains]: [await userQueries.getModelName()] },
 			})
-			const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organization_id)
+			const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organizations[0].id)
 
 			const processDbResponse = await utils.processDbResponse(user, prunedEntities)
 			return responses.successResponse({
@@ -527,7 +517,7 @@ module.exports = class UserHelper {
 	 * @param {Object} bodyData - it contains user preferred language
 	 * @returns {JSON} - updated user preferred languages response
 	 */
-	static async setLanguagePreference(bodyData, id, orgId, tenantCode) {
+	static async setLanguagePreference(bodyData, id, organizationCode, tenantCode) {
 		try {
 			let skipRequiredValidation = true
 			const user = await userQueries.findOne({ id: id, tenant_code: tenantCode })
@@ -538,15 +528,12 @@ module.exports = class UserHelper {
 					responseCode: 'UNAUTHORIZED',
 				})
 			}
-			let defaultOrg = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE, tenant_code: tenantCode },
-				{ attributes: ['id'] }
-			)
-			let defaultOrgId = defaultOrg.id
+			let defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
+
 			let userModel = await userQueries.getColumns()
 			const filter = {
 				status: common.ACTIVE_STATUS,
-				organization_id: { [Op.in]: [orgId, defaultOrgId] },
+				organization_code: { [Op.in]: [organizationCode, defaultOrganizationCode] },
 				model_names: { [Op.contains]: [userModel] },
 				value: 'preferred_language',
 				tenant_code: tenantCode,
diff --git a/src/validators/v1/entity-type.js b/src/validators/v1/entity-type.js
index 46a796d8b..35f4b9c82 100644
--- a/src/validators/v1/entity-type.js
+++ b/src/validators/v1/entity-type.js
@@ -72,8 +72,7 @@ module.exports = {
 			.withMessage('model_names must be an array with at least one element')
 
 		req.checkBody('model_names.*')
-			.isIn(['Session', 'MentorExtension', 'UserExtension'])
-			.withMessage('model_names must be in Session,MentorExtension,UserExtension')
+		isIn(['User']).withMessage('model_names must be in User')
 	},
 
 	read: (req) => {
diff --git a/src/validators/v1/entity.js b/src/validators/v1/entity.js
index 3a255af37..9872b88ad 100644
--- a/src/validators/v1/entity.js
+++ b/src/validators/v1/entity.js
@@ -74,22 +74,41 @@ module.exports = {
 
 	read: (req) => {
 		req.checkQuery('id')
-			.trim()
 			.optional()
+			.trim()
 			.notEmpty()
 			.withMessage('id param is empty')
 			.isNumeric()
 			.withMessage('id param is invalid, must be an integer')
 
 		req.checkQuery('value')
-			.trim()
 			.optional()
+			.trim()
 			.notEmpty()
 			.withMessage('value field is empty')
 			.matches(/^[A-Za-z0-9 ]+$/)
 			.withMessage('value is invalid, must not contain spaces')
-	},
 
+		req.checkQuery('entity_type_id')
+			.optional()
+			.trim()
+			.notEmpty()
+			.withMessage('entity_type_id is empty')
+			.isNumeric()
+			.withMessage('entity_type_id must be numeric')
+
+		// Custom validator to ensure either `id` OR (`value` + `entity_type_id`) is present
+		req.checkQuery('').custom(() => {
+			const id = req.query.id
+			const value = req.query.value
+			const entityTypeId = req.query.entity_type_id
+
+			if (id) return true
+			if (value && entityTypeId) return true
+
+			throw new Error('Either id OR (value and entity_type_id) must be provided')
+		})
+	},
 	delete: (req) => {
 		req.checkParams('id').notEmpty().withMessage('id param is empty')
 	},

From 1c9708ecb694fcf44ecf56688627f15ed4af20ac Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Wed, 30 Jul 2025 14:31:23 +0530
Subject: [PATCH 11/55] coderabit review comments addressed

---
 .../migrations/20250729064710-org-code-fix.js      | 14 --------------
 src/validators/v1/organization.js                  |  2 +-
 2 files changed, 1 insertion(+), 15 deletions(-)

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
index 29ee00427..8b155916e 100644
--- a/src/database/migrations/20250729064710-org-code-fix.js
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -50,18 +50,6 @@ module.exports = {
 				})
 				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
 
-				table = 'organization_user_invites'
-				fk_name = 'fk_org_user_invites_org_code'
-				fkey = '(organization_code, tenant_code)'
-				refTable = 'organizations'
-				refKey = '(code, tenant_code)'
-				await queryInterface.sequelize.query(disableFK(table, fk_name), {
-					type: Sequelize.QueryTypes.RAW,
-					raw: true,
-					transaction,
-				})
-				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
-
 				table = 'organization_user_invites'
 				fk_name = 'fk_org_user_invites_organization_id'
 				fkey = '(organization_code, tenant_code)'
@@ -151,7 +139,6 @@ module.exports = {
 					raw: true,
 					transaction,
 				})
-				console.log(fetchOrgs)
 
 				// Re-enable foreign key constraints
 				let fk_retainerPromise = []
@@ -169,7 +156,6 @@ module.exports = {
 
 				// Commit the transaction
 				await transaction.commit()
-				console.log('Transaction committed successfully.')
 			}
 		} catch (error) {
 			// Rollback transaction on error
diff --git a/src/validators/v1/organization.js b/src/validators/v1/organization.js
index fd80a1708..f81a6d428 100644
--- a/src/validators/v1/organization.js
+++ b/src/validators/v1/organization.js
@@ -16,7 +16,7 @@ module.exports = {
 			.trim()
 			.notEmpty()
 			.withMessage('code field is empty')
-			.matches(/^[a-z0-9]+$/)
+			.matches(/^[a-z0-9_]+$/)
 			.withMessage('code is invalid. Only lowercase alphanumeric characters allowed')
 		req.checkBody('tenant_code').trim().notEmpty().withMessage('tenant_code field is empty')
 		req.checkBody('registration_codes')

From 7da1e65f1117e3b338ac667c7420d3a55deedcea Mon Sep 17 00:00:00 2001
From: Nevil <nevil@tunerlabs.com>
Date: Wed, 30 Jul 2025 14:55:44 +0530
Subject: [PATCH 12/55] add: default config

---
 .coderabbit.yaml | 175 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 175 insertions(+)
 create mode 100644 .coderabbit.yaml

diff --git a/.coderabbit.yaml b/.coderabbit.yaml
new file mode 100644
index 000000000..1d6443f45
--- /dev/null
+++ b/.coderabbit.yaml
@@ -0,0 +1,175 @@
+language: en-US
+tone_instructions: ''
+early_access: false
+enable_free_tier: true
+reviews:
+  profile: chill
+  request_changes_workflow: false
+  high_level_summary: true
+  high_level_summary_placeholder: '@coderabbitai summary'
+  high_level_summary_in_walkthrough: false
+  auto_title_placeholder: '@coderabbitai'
+  auto_title_instructions: ''
+  review_status: true
+  commit_status: true
+  fail_commit_status: false
+  collapse_walkthrough: false
+  changed_files_summary: true
+  sequence_diagrams: true
+  estimate_code_review_effort: true
+  assess_linked_issues: true
+  related_issues: true
+  related_prs: true
+  suggested_labels: true
+  auto_apply_labels: false
+  suggested_reviewers: true
+  auto_assign_reviewers: false
+  poem: true
+  labeling_instructions: []
+  path_filters: []
+  path_instructions: []
+  abort_on_close: true
+  disable_cache: false
+  auto_review:
+    enabled: true
+    auto_incremental_review: true
+    ignore_title_keywords: []
+    labels: []
+    drafts: false
+    base_branches: []
+  finishing_touches:
+    docstrings:
+      enabled: true
+    unit_tests:
+      enabled: true
+  pre_merge_checks:
+    docstrings:
+      mode: warning
+      threshold: 80
+    title:
+      mode: warning
+      requirements: ''
+    description:
+      mode: warning
+    issue_assessment:
+      mode: warning
+  tools:
+    ast-grep:
+      rule_dirs: []
+      util_dirs: []
+      essential_rules: true
+      packages: []
+    shellcheck:
+      enabled: true
+    ruff:
+      enabled: true
+    markdownlint:
+      enabled: true
+    github-checks:
+      enabled: true
+      timeout_ms: 90000
+    languagetool:
+      enabled: true
+      enabled_rules: []
+      disabled_rules: []
+      enabled_categories: []
+      disabled_categories: []
+      enabled_only: false
+      level: default
+    biome:
+      enabled: true
+    hadolint:
+      enabled: true
+    swiftlint:
+      enabled: true
+    phpstan:
+      enabled: true
+      level: default
+    phpmd:
+      enabled: true
+    phpcs:
+      enabled: true
+    golangci-lint:
+      enabled: true
+    yamllint:
+      enabled: true
+    gitleaks:
+      enabled: true
+    checkov:
+      enabled: true
+    detekt:
+      enabled: true
+    eslint:
+      enabled: true
+    flake8:
+      enabled: true
+    rubocop:
+      enabled: true
+    buf:
+      enabled: true
+    regal:
+      enabled: true
+    actionlint:
+      enabled: true
+    pmd:
+      enabled: true
+    cppcheck:
+      enabled: true
+    semgrep:
+      enabled: true
+    circleci:
+      enabled: true
+    clippy:
+      enabled: true
+    sqlfluff:
+      enabled: true
+    prismaLint:
+      enabled: true
+    pylint:
+      enabled: true
+    oxc:
+      enabled: true
+    shopifyThemeCheck:
+      enabled: true
+    luacheck:
+      enabled: true
+    brakeman:
+      enabled: true
+    dotenvLint:
+      enabled: true
+    htmlhint:
+      enabled: true
+    checkmake:
+      enabled: true
+chat:
+  auto_reply: true
+  integrations:
+    jira:
+      usage: auto
+    linear:
+      usage: auto
+knowledge_base:
+  opt_out: false
+  web_search:
+    enabled: true
+  code_guidelines:
+    enabled: true
+    filePatterns: []
+  learnings:
+    scope: auto
+  issues:
+    scope: auto
+  jira:
+    usage: auto
+    project_keys: []
+  linear:
+    usage: auto
+    team_keys: []
+  pull_requests:
+    scope: auto
+code_generation:
+  docstrings:
+    language: en-US
+    path_instructions: []
+  unit_tests:
+    path_instructions: []

From 33fe6c8b569893095104ac0a12d20ea3ad1d629e Mon Sep 17 00:00:00 2001
From: Nevil <nevil@tunerlabs.com>
Date: Wed, 30 Jul 2025 14:58:42 +0530
Subject: [PATCH 13/55] update: coderabbit config

---
 .coderabbit.yaml | 37 +++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/.coderabbit.yaml b/.coderabbit.yaml
index 1d6443f45..7a7a654aa 100644
--- a/.coderabbit.yaml
+++ b/.coderabbit.yaml
@@ -1,3 +1,5 @@
+# yaml-language-server: $schema=https://storage.googleapis.com/coderabbit_public_assets/schema.v2.json
+
 language: en-US
 tone_instructions: ''
 early_access: false
@@ -26,17 +28,44 @@ reviews:
   auto_assign_reviewers: false
   poem: true
   labeling_instructions: []
-  path_filters: []
-  path_instructions: []
+
+  #Exclude non-source code directories from reviews.
+  path_filters:
+    - "!deployment/**"
+    - "!dev-ops/**"
+    - "!release-notes/**"
+    - "!src/api-doc/**"
+    - "!src/assets/**"
+    - "!src/configs/**"
+    - "!src/public/**"
+    - "!src/temp/**"
+    - "!**/*.md"
+
+  # Add specific review instructions for critical paths.
+  path_instructions:
+    - path: "src/services/**"
+      instructions: "This is core business logic. Please check for correctness, efficiency, and potential edge cases."
+    - path: "src/controllers/**"
+      instructions: "These are API controllers. Focus on request/response handling, security (auth middleware usage), and proper status codes."
+    - path: "src/database/queries/**"
+      instructions: "Review database queries for performance. Check for N+1 problems and ensure indexes can be used."
+    - path: "src/validators/**"
+      instructions: "Validate all incoming data thoroughly. Check for missing or incomplete validation rules."
+    - path: "src/middlewares/**"
+      instructions: "This is security-sensitive middleware. Scrutinize for potential vulnerabilities."
+
   abort_on_close: true
   disable_cache: false
   auto_review:
     enabled: true
     auto_incremental_review: true
-    ignore_title_keywords: []
+    ignore_title_keywords: ["WIP", "skip-review"]
     labels: []
     drafts: false
-    base_branches: []
+    # Target reviews to development branches.
+    base_branches:
+      - "develop"
+
   finishing_touches:
     docstrings:
       enabled: true

From 16eb153e67efb8598a49144791a162c4afaf0cd5 Mon Sep 17 00:00:00 2001
From: Nevil <nevil@tunerlabs.com>
Date: Wed, 30 Jul 2025 15:43:03 +0530
Subject: [PATCH 14/55] removed: tools

---
 .coderabbit.yaml | 88 ++++++++++--------------------------------------
 1 file changed, 18 insertions(+), 70 deletions(-)

diff --git a/.coderabbit.yaml b/.coderabbit.yaml
index 7a7a654aa..d611ec6b2 100644
--- a/.coderabbit.yaml
+++ b/.coderabbit.yaml
@@ -83,93 +83,41 @@ reviews:
     issue_assessment:
       mode: warning
   tools:
-    ast-grep:
-      rule_dirs: []
-      util_dirs: []
-      essential_rules: true
-      packages: []
-    shellcheck:
-      enabled: true
-    ruff:
-      enabled: true
-    markdownlint:
-      enabled: true
-    github-checks:
-      enabled: true
-      timeout_ms: 90000
-    languagetool:
-      enabled: true
-      enabled_rules: []
-      disabled_rules: []
-      enabled_categories: []
-      disabled_categories: []
-      enabled_only: false
-      level: default
-    biome:
-      enabled: true
-    hadolint:
-      enabled: true
-    swiftlint:
-      enabled: true
-    phpstan:
-      enabled: true
-      level: default
-    phpmd:
-      enabled: true
-    phpcs:
+    eslint:
       enabled: true
-    golangci-lint:
+    semgrep:
       enabled: true
-    yamllint:
+    dotenvLint:
       enabled: true
-    gitleaks:
+    sqlfluff:
       enabled: true
     checkov:
       enabled: true
-    detekt:
-      enabled: true
-    eslint:
-      enabled: true
-    flake8:
-      enabled: true
-    rubocop:
+    gitleaks:
       enabled: true
-    buf:
+    hadolint:
       enabled: true
-    regal:
+    shellcheck:
       enabled: true
     actionlint:
       enabled: true
-    pmd:
-      enabled: true
-    cppcheck:
-      enabled: true
-    semgrep:
-      enabled: true
     circleci:
       enabled: true
-    clippy:
-      enabled: true
-    sqlfluff:
-      enabled: true
-    prismaLint:
-      enabled: true
-    pylint:
-      enabled: true
-    oxc:
-      enabled: true
-    shopifyThemeCheck:
-      enabled: true
-    luacheck:
-      enabled: true
-    brakeman:
+    github-checks:
       enabled: true
-    dotenvLint:
+    markdownlint:
       enabled: true
-    htmlhint:
+    yamllint:
       enabled: true
-    checkmake:
+    biome:
       enabled: true
+    languagetool:
+      enabled: false  # optional
+    ast-grep:
+      essential_rules: true
+      rule_dirs: []
+      util_dirs: []
+      packages: []
 chat:
   auto_reply: true
   integrations:

From 17058065500fb85ddee8e7ec25a7103c6fc20a70 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 12:11:30 +0530
Subject: [PATCH 15/55] fixed: an issue with func param name

---
 src/utils/notification.js | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/utils/notification.js b/src/utils/notification.js
index 4b7a44a86..1712b6334 100644
--- a/src/utils/notification.js
+++ b/src/utils/notification.js
@@ -7,13 +7,17 @@ const NOTIFICATION_MODE = process.env.NOTIFICATION_MODE
 const { sendNotification } = require('../requests/notification')
 const common = require('@constants/common')
 
-async function sendEmailNotification({ emailId, templateCode, variables, tenantCode, organization_code = '' }) {
+async function sendEmailNotification({ emailId, templateCode, variables, tenantCode, organizationCode = '' }) {
 	try {
 		if (!emailId) return
 
-		const templateData =
-			(await notificationTemplateQueries.findOneEmailTemplate(templateCode, organization_code, tenantCode)) || {}
-		if (Object.keys(templateData).length <= 0) {
+		const templateData = await notificationTemplateQueries.findOneEmailTemplate(
+			templateCode,
+			organizationCode,
+			tenantCode
+		)
+
+		if (!templateData) {
 			console.warn(`Email template not found for code: ${templateCode}`)
 			return
 		}
@@ -47,13 +51,13 @@ async function sendEmailNotification({ emailId, templateCode, variables, tenantC
 	}
 }
 
-async function sendSMSNotification({ phoneNumber, templateCode, variables, tenantCode, organization_code = '' }) {
+async function sendSMSNotification({ phoneNumber, templateCode, variables, tenantCode, organizationCode = '' }) {
 	try {
 		if (!phoneNumber) return
 
 		const templateData = await notificationTemplateQueries.findOneSMSTemplate(
 			templateCode,
-			organization_code,
+			organizationCode,
 			tenantCode
 		)
 		if (!templateData) {

From f2a82be0dc896432ae2a7f629e8b19659e2e19fe Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 12:12:14 +0530
Subject: [PATCH 16/55] fixed: return in db queries

---
 src/database/queries/notificationTemplate.js | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/database/queries/notificationTemplate.js b/src/database/queries/notificationTemplate.js
index d2a7258ec..7aed3e729 100644
--- a/src/database/queries/notificationTemplate.js
+++ b/src/database/queries/notificationTemplate.js
@@ -2,7 +2,6 @@
 const NotificationTemplate = require('@database/models/index').NotificationTemplate
 const { Op } = require('sequelize')
 const common = require('@constants/common')
-const organizationQueries = require('@database/queries/organization')
 
 exports.create = async (data) => {
 	try {
@@ -20,7 +19,7 @@ exports.findOne = async (filter, options = {}) => {
 			raw: true,
 		})
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
@@ -98,7 +97,7 @@ exports.findOneEmailTemplate = async (code, orgCode = null, tenantCode) => {
 		}
 		return templateData
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
@@ -116,7 +115,7 @@ exports.getEmailHeader = async (header) => {
 		})
 		return headerData
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
@@ -134,7 +133,7 @@ exports.getEmailFooter = async (footer) => {
 		})
 		return headerData
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
@@ -148,7 +147,7 @@ exports.updateTemplate = async (filter, update, options = {}) => {
 
 		return template
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
@@ -178,7 +177,7 @@ exports.findAllNotificationTemplates = async (filter, options = {}) => {
 
 		return templates
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 

From e5388899fdf3a829ffdf341c998d26b901598c8c Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 12:12:50 +0530
Subject: [PATCH 17/55] fix: issue with app url in role req email

---
 src/services/org-admin.js | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/src/services/org-admin.js b/src/services/org-admin.js
index a444284ec..5cf1fd400 100644
--- a/src/services/org-admin.js
+++ b/src/services/org-admin.js
@@ -23,6 +23,7 @@ const { eventBroadcaster } = require('@helpers/eventBroadcaster')
 const { Queue } = require('bullmq')
 const { Op } = require('sequelize')
 const UserCredentialQueries = require('@database/queries/userCredential')
+const tenantDomainQueries = require('@database/queries/tenantDomain')
 const emailEncryption = require('@utils/emailEncryption')
 const responses = require('@helpers/responses')
 const notificationUtils = require('@utils/notification')
@@ -366,7 +367,6 @@ module.exports = class OrgAdminHelper {
 				tenant_code: tokenInformation.tenant_code,
 			})
 
-			console.log(isApproved, 'isApproved')
 			if (isApproved) {
 				await updateRoleForApprovedRequest(
 					requestDetails,
@@ -376,9 +376,13 @@ module.exports = class OrgAdminHelper {
 				)
 			}
 
-			console.log(shouldSendEmail, 'shouldSendEmail')
 			if (shouldSendEmail) {
-				await sendRoleRequestStatusEmail(user, bodyData.status, tokenInformation.organization_code)
+				await sendRoleRequestStatusEmail(
+					user,
+					bodyData.status,
+					tokenInformation.organization_code,
+					tokenInformation.tenant_code
+				)
 			}
 
 			return responses.successResponse({
@@ -607,23 +611,28 @@ function updateRoleForApprovedRequest(requestDetails, user, tenantCode, orgCode)
 	})
 }
 
-async function sendRoleRequestStatusEmail(userDetails, status, orgCode) {
+async function sendRoleRequestStatusEmail(userDetails, status, organizationCode, tenantCode) {
 	try {
 		const plaintextEmailId = emailEncryption.decrypt(userDetails.email)
 
 		if (status === common.ACCEPTED_STATUS) {
 			if (plaintextEmailId) {
+				const tenantDomain = await tenantDomainQueries.findOne(
+					{ tenant_code: tenantCode },
+					{ attributes: ['domain'] }
+				)
+
 				notificationUtils.sendEmailNotification({
 					emailId: plaintextEmailId,
 					templateCode: process.env.MENTOR_REQUEST_ACCEPTED_EMAIL_TEMPLATE_CODE,
 					variables: {
 						name: userDetails.name,
 						appName: process.env.APP_NAME,
-						orgName: _.find(userDetails.organizations, { code: orgCode })?.name || '',
-						portalURL: process.env.PORTAL_URL,
+						orgName: _.find(userDetails.organizations, { code: organizationCode })?.name || '',
+						portalURL: tenantDomain,
 					},
 					tenantCode: userDetails.tenant_code,
-					organization_code: orgCode || null,
+					organization_code: organizationCode || null,
 				})
 			}
 		} else if (status === common.REJECTED_STATUS) {
@@ -640,10 +649,10 @@ async function sendRoleRequestStatusEmail(userDetails, status, orgCode) {
 				})
 			}
 		}
-		console.log({ name: userDetails.name, appName: process.env.APP_NAME, orgName: organization.name }, 'payload')
 
 		return { success: true }
 	} catch (error) {
+		console.error(error)
 		return error
 	}
 }

From 59606a3730106561961a80ac40137025a5121478 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 13:58:28 +0530
Subject: [PATCH 18/55] added: await to bcryptJs

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

diff --git a/src/services/account.js b/src/services/account.js
index 0e3443e64..09bfe915f 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -727,7 +727,7 @@ module.exports = class AccountHelper {
 			}
 
 			// Verify password
-			const isPasswordCorrect = bcryptJs.compare(bodyData.password, user.password)
+			const isPasswordCorrect = await bcryptJs.compare(bodyData.password, user.password)
 			if (!isPasswordCorrect) {
 				return responses.failureResponse({
 					message: 'IDENTIFIER_OR_PASSWORD_INVALID',

From 11ff426a5c1cf2c2c5d2cb9a12614ed43e92e3c5 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 14:02:19 +0530
Subject: [PATCH 19/55] updated: Align parameter ordering across
 entityTypeService methods

---
 src/controllers/v1/entity-type.js | 4 ++--
 src/services/entityType.js        | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/controllers/v1/entity-type.js b/src/controllers/v1/entity-type.js
index 7cfc5866e..bfb565cc6 100644
--- a/src/controllers/v1/entity-type.js
+++ b/src/controllers/v1/entity-type.js
@@ -65,9 +65,9 @@ module.exports = class Entity {
 				)
 			}
 			return await entityTypeService.readAllSystemEntityTypes(
-				req.decodedToken.organization_id,
 				req.decodedToken.organization_code,
-				req.decodedToken.tenant_code
+				req.decodedToken.tenant_code,
+				req.decodedToken.organization_id
 			)
 		} catch (error) {
 			return error
diff --git a/src/services/entityType.js b/src/services/entityType.js
index 5aa88079d..955685af5 100644
--- a/src/services/entityType.js
+++ b/src/services/entityType.js
@@ -91,7 +91,7 @@ module.exports = class EntityHelper {
 		}
 	}
 
-	static async readAllSystemEntityTypes(organizationId, organizationCode, tenantCode) {
+	static async readAllSystemEntityTypes(organizationCode, tenantCode, organizationId) {
 		try {
 			const attributes = ['value', 'label', 'id', 'organization_code']
 

From eb47e49bb820e4f7125af17a434d4d42845feab2 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 14:09:28 +0530
Subject: [PATCH 20/55] =?UTF-8?q?updated:=20the=20transitional=20dual?=
 =?UTF-8?q?=E2=80=90identifier=20strategy=20in=20the=20EntityType=20model?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/database/models/entityType.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/database/models/entityType.js b/src/database/models/entityType.js
index 427825d7d..2f03fb4c9 100644
--- a/src/database/models/entityType.js
+++ b/src/database/models/entityType.js
@@ -16,6 +16,9 @@ module.exports = (sequelize, DataTypes) => {
 			updated_by: { type: DataTypes.INTEGER, allowNull: true },
 			allow_filtering: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
 			data_type: { type: DataTypes.STRING, allowNull: false, defaultValue: 'STRING' },
+			// NOTE: `organization_id` is temporarily retained only for the backfill
+			// and restore migration process. It is planned to be removed once that
+			// process is complete. Use `organization_code` for all ongoing references.
 			organization_id: { type: DataTypes.INTEGER, allowNull: false },
 			organization_code: {
 				type: DataTypes.STRING,

From f2cb602c9dd77b0ec79e30a4f7fa59ba0c15daeb Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 14:11:34 +0530
Subject: [PATCH 21/55] added: organization_code in the unique index on
 entities

---
 .../migrations/20250723183753-add-entity-types-constraints.js   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/database/migrations/20250723183753-add-entity-types-constraints.js b/src/database/migrations/20250723183753-add-entity-types-constraints.js
index 1ac6179e8..60194f026 100644
--- a/src/database/migrations/20250723183753-add-entity-types-constraints.js
+++ b/src/database/migrations/20250723183753-add-entity-types-constraints.js
@@ -17,7 +17,7 @@ module.exports = {
 			await queryInterface.sequelize.query(
 				`
         CREATE UNIQUE INDEX unique_entities_value_type_tenant
-        ON entities (value, entity_type_id, tenant_code)
+        ON entities (value, entity_type_id,organization_code, tenant_code)
         WHERE deleted_at IS NULL;
       `,
 				{ transaction }

From 728ebc718f5c381b72855008224a783655e0f6d3 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 31 Jul 2025 14:16:35 +0530
Subject: [PATCH 22/55] fixed: incorrect string comparison with numeric value

---
 src/services/entities.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/services/entities.js b/src/services/entities.js
index 7e684f943..f517d4af1 100644
--- a/src/services/entities.js
+++ b/src/services/entities.js
@@ -153,7 +153,8 @@ module.exports = class EntityHelper {
 	static async delete(id, organizationCode, tenantCode) {
 		try {
 			const deleteCount = await entityQueries.deleteOneEntity(id, organizationCode, tenantCode)
-			if (deleteCount === '0') {
+
+			if (deleteCount === 0) {
 				return responses.failureResponse({
 					message: 'ENTITY_NOT_FOUND',
 					statusCode: httpStatusCode.bad_request,

From b7d11d503d7aa32a8fe21143de3d46d8b7db824e Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Fri, 1 Aug 2025 11:52:31 +0530
Subject: [PATCH 23/55] updated: inheritEntityType endpoint

---
 src/controllers/v1/org-admin.js |  3 +-
 src/locales/en.json             |  2 +-
 src/services/org-admin.js       | 58 +++++++++++++++++----------------
 3 files changed, 33 insertions(+), 30 deletions(-)

diff --git a/src/controllers/v1/org-admin.js b/src/controllers/v1/org-admin.js
index 78e2c152c..287852060 100644
--- a/src/controllers/v1/org-admin.js
+++ b/src/controllers/v1/org-admin.js
@@ -193,9 +193,10 @@ module.exports = class OrgAdmin {
 					responseCode: 'CLIENT_ERROR',
 				})
 			}
-			let entityTypeDetails = orgAdminService.inheritEntityType(
+			let entityTypeDetails = await orgAdminService.inheritEntityType(
 				req.body.entity_type_value,
 				req.body.target_entity_type_label,
+				req.decodedToken.organization_code,
 				req.decodedToken.organization_id,
 				req.decodedToken.id
 			)
diff --git a/src/locales/en.json b/src/locales/en.json
index 8bb8d1895..5a5f4b231 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -91,7 +91,7 @@
 	"EMAIL_OR_ID_REQUIRED": "Either 'id' or 'email' fields is required",
 	"REQUEST_NOT_FOUND": "Role Change Request not found.",
 	"FAILED_TO_ASSIGN_AS_ADMIN": "User belongs to different organization",
-	"USER_IS_FROM_DEFAULT_ORG": "User is from default org",
+	"USER_IS_FROM_DEFAULT_ORG": "Users from the default organization are not permitted to proceed with this action.",
 	"NOTIFICATION_TEMPLATE_ALREADY_EXISTS": "Notification template already exists",
 	"NOTIFICATION_TEMPLATE_CREATED_SUCCESSFULLY": "Notification template created successfully",
 	"NOTIFICATION_TEMPLATE_UPDATED_SUCCESSFULLY": "Notification template updated successfully",
diff --git a/src/services/org-admin.js b/src/services/org-admin.js
index 5cf1fd400..48db7685f 100644
--- a/src/services/org-admin.js
+++ b/src/services/org-admin.js
@@ -462,40 +462,37 @@ module.exports = class OrgAdminHelper {
 	}
 
 	/**
-	 * @description 					- Inherit new entity type from an existing default org's entityType.
+	 * @description                         - Inherit new entity type from an existing default org's entityType.
 	 * @method
-	 * @name 							- inheritEntityType
-	 * @param {String} entityValue 		- Entity type value
-	 * @param {String} entityLabel 		- Entity type label
-	 * @param {Integer} userOrgId 		- User org id
-	 * @param {Integer} userId 			- Userid
-	 * @returns {Promise<Object>} 		- A Promise that resolves to a response object.
+	 * @name                                - inheritEntityType
+	 * @param {String} entityValue          - Entity type value
+	 * @param {String} entityLabel          - Entity type label
+	 * @param {String} userOrganizationCode - User's organization code
+	 * @param {Integer} userOrganizationId  - User's organization ID
+	 * @param {Integer} userId              - User ID
+	 * @returns {Promise<Object>}           - A Promise that resolves to a response object.
 	 */
-
-	static async inheritEntityType(entityValue, entityLabel, userOrgId, userId) {
+	static async inheritEntityType(entityValue, entityLabel, userOrganizationCode, userOrganizationId, userId) {
 		try {
-			let defaultOrgId = await organizationQueries.findOne(
-				{ code: process.env.DEFAULT_ORGANISATION_CODE },
-				{ attributes: ['id'] }
-			)
-			defaultOrgId = defaultOrgId.id
-			if (defaultOrgId === userOrgId) {
+			// Prevent inheriting from the default org
+			if (process.env.DEFAULT_ORGANISATION_CODE === userOrganizationCode) {
 				return responses.failureResponse({
 					message: 'USER_IS_FROM_DEFAULT_ORG',
 					statusCode: httpStatusCode.bad_request,
 					responseCode: 'CLIENT_ERROR',
 				})
 			}
-			// Fetch entity type data using defaultOrgId and entityValue
+
+			// Fetch entity type data from default org
 			const filter = {
 				value: entityValue,
-				organization_id: defaultOrgId,
+				organization_code: process.env.DEFAULT_ORGANISATION_CODE,
 				allow_filtering: true,
 			}
 
-			let entityTypeDetails = await entityTypeQueries.findOneEntityType(filter)
+			const entityTypeDetails = await entityTypeQueries.findOneEntityType(filter)
 
-			// If no matching data found return failure response
+			// If no matching data found, return failure
 			if (!entityTypeDetails) {
 				return responses.failureResponse({
 					message: 'ENTITY_TYPE_NOT_FOUND',
@@ -504,23 +501,28 @@ module.exports = class OrgAdminHelper {
 				})
 			}
 
-			// Build data for inheriting entityType
-			entityTypeDetails.parent_id = entityTypeDetails.organization_id
-			entityTypeDetails.label = entityLabel
-			entityTypeDetails.organization_id = userOrgId
-			entityTypeDetails.created_by = userId
-			entityTypeDetails.updated_by = userId
-			delete entityTypeDetails.id
+			// Prepare new entity type object (clone and modify)
+			const newEntityType = {
+				...entityTypeDetails,
+				parent_id: entityTypeDetails.id,
+				label: entityLabel,
+				organization_code: userOrganizationCode,
+				organization_id: userOrganizationId,
+				created_by: userId,
+				updated_by: userId,
+			}
+			delete newEntityType.id
 
 			// Create new inherited entity type
-			let inheritedEntityType = await entityTypeQueries.createEntityType(entityTypeDetails)
+			const inheritedEntityType = await entityTypeQueries.createEntityType(newEntityType)
+
 			return responses.successResponse({
 				statusCode: httpStatusCode.created,
 				message: 'ENTITY_TYPE_CREATED_SUCCESSFULLY',
 				result: inheritedEntityType,
 			})
 		} catch (error) {
-			console.log(error)
+			console.error('Error in inheritEntityType:', error)
 			throw error
 		}
 	}

From c5a8d3f1192ddbc9dab9d16d3db1d19461ba0e7a Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Fri, 1 Aug 2025 12:45:09 +0530
Subject: [PATCH 24/55] updated: entity_types unique index

---
 .../20250723183753-add-entity-types-constraints.js    | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/database/migrations/20250723183753-add-entity-types-constraints.js b/src/database/migrations/20250723183753-add-entity-types-constraints.js
index 60194f026..37b4a0042 100644
--- a/src/database/migrations/20250723183753-add-entity-types-constraints.js
+++ b/src/database/migrations/20250723183753-add-entity-types-constraints.js
@@ -7,6 +7,13 @@ module.exports = {
 			// Drop existing UNIQUE constraint
 			await queryInterface.sequelize.query(
 				`
+        ALTER TABLE entity_types
+        DROP CONSTRAINT IF EXISTS unique_value_org_id_tenant_code;
+      `,
+				{ transaction }
+			)
+			await queryInterface.sequelize.query(
+				`
         ALTER TABLE entities
         DROP CONSTRAINT IF EXISTS unique_entities_value_type_tenant;
       `,
@@ -16,8 +23,8 @@ module.exports = {
 			// Add partial unique index
 			await queryInterface.sequelize.query(
 				`
-        CREATE UNIQUE INDEX unique_entities_value_type_tenant
-        ON entities (value, entity_type_id,organization_code, tenant_code)
+        CREATE UNIQUE INDEX unique_value_org_code_tenant_code
+        ON entity_types (value,organization_code, tenant_code)
         WHERE deleted_at IS NULL;
       `,
 				{ transaction }

From 6b551b63399a86d42a1ad78b592253cb09ea8c49 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Fri, 1 Aug 2025 16:07:54 +0530
Subject: [PATCH 25/55] minor fix for QA DB

---
 .../migrations/20250729064710-org-code-fix.js | 42 +++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
index 8b155916e..094bff12a 100644
--- a/src/database/migrations/20250729064710-org-code-fix.js
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -1,5 +1,36 @@
 const { Sequelize } = require('sequelize')
 
+/*
+BEGIN;
+
+-- Delete from user_organization_roles
+DELETE FROM user_organization_roles WHERE organization_code = 'sot' RETURNING *;
+
+-- Delete from user_organizations and capture user_id for users deletion
+WITH deleted_user_orgs AS (
+    DELETE FROM user_organizations WHERE organization_code = 'sot' RETURNING user_id
+)
+-- Delete from users using user_id from user_organizations
+DELETE FROM users WHERE id IN (SELECT user_id FROM deleted_user_orgs) RETURNING *;
+
+-- Delete from organizations
+DELETE FROM organizations WHERE code = 'sot' RETURNING *;
+
+-- Delete from organization_user_invites
+DELETE FROM organization_user_invites WHERE organization_code = 'sot' RETURNING *;
+
+-- Delete from notification_templates
+DELETE FROM notification_templates WHERE organization_code = 'sot' RETURNING *;
+
+-- Delete from organization_features
+DELETE FROM organization_features WHERE organization_code = 'sot' RETURNING *;
+
+-- Delete from organization_registration_codes
+DELETE FROM organization_registration_codes WHERE organization_code = 'sot' RETURNING *;
+
+COMMIT;
+*/
+
 module.exports = {
 	async up(queryInterface, Sequelize) {
 		let transaction
@@ -60,6 +91,17 @@ module.exports = {
 					raw: true,
 					transaction,
 				})
+				table = 'organization_user_invites'
+				fk_name = 'fk_org_user_invites_org_code'
+				fkey = '(organization_code, tenant_code)'
+				refTable = 'organizations'
+				refKey = '(code, tenant_code)'
+				await queryInterface.sequelize.query(disableFK(table, fk_name), {
+					type: Sequelize.QueryTypes.RAW,
+					raw: true,
+					transaction,
+				})
+				fk_retainer.push(enableFK(table, fk_name, fkey, refTable, refKey))
 
 				table = 'user_organization_roles'
 				fk_name = 'fk_user_org_roles_user_organizations'

From 867e2228011afa96782aaff22143d22c03f0b049 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Fri, 1 Aug 2025 18:08:18 +0530
Subject: [PATCH 26/55] added Script to hard - delete organization and related
 data

---
 .../migrations/20250729064710-org-code-fix.js |  31 ---
 .../deleted-org-data-clean-up/clean.js        | 256 ++++++++++++++++++
 2 files changed, 256 insertions(+), 31 deletions(-)
 create mode 100644 src/scripts/deleted-org-data-clean-up/clean.js

diff --git a/src/database/migrations/20250729064710-org-code-fix.js b/src/database/migrations/20250729064710-org-code-fix.js
index 094bff12a..aa4889c2c 100644
--- a/src/database/migrations/20250729064710-org-code-fix.js
+++ b/src/database/migrations/20250729064710-org-code-fix.js
@@ -1,36 +1,5 @@
 const { Sequelize } = require('sequelize')
 
-/*
-BEGIN;
-
--- Delete from user_organization_roles
-DELETE FROM user_organization_roles WHERE organization_code = 'sot' RETURNING *;
-
--- Delete from user_organizations and capture user_id for users deletion
-WITH deleted_user_orgs AS (
-    DELETE FROM user_organizations WHERE organization_code = 'sot' RETURNING user_id
-)
--- Delete from users using user_id from user_organizations
-DELETE FROM users WHERE id IN (SELECT user_id FROM deleted_user_orgs) RETURNING *;
-
--- Delete from organizations
-DELETE FROM organizations WHERE code = 'sot' RETURNING *;
-
--- Delete from organization_user_invites
-DELETE FROM organization_user_invites WHERE organization_code = 'sot' RETURNING *;
-
--- Delete from notification_templates
-DELETE FROM notification_templates WHERE organization_code = 'sot' RETURNING *;
-
--- Delete from organization_features
-DELETE FROM organization_features WHERE organization_code = 'sot' RETURNING *;
-
--- Delete from organization_registration_codes
-DELETE FROM organization_registration_codes WHERE organization_code = 'sot' RETURNING *;
-
-COMMIT;
-*/
-
 module.exports = {
 	async up(queryInterface, Sequelize) {
 		let transaction
diff --git a/src/scripts/deleted-org-data-clean-up/clean.js b/src/scripts/deleted-org-data-clean-up/clean.js
new file mode 100644
index 000000000..3a576e6f0
--- /dev/null
+++ b/src/scripts/deleted-org-data-clean-up/clean.js
@@ -0,0 +1,256 @@
+const { Sequelize } = require('sequelize')
+let Table = require('cli-table')
+require('dotenv').config({ path: '../../.env' })
+
+// Environment setup
+const nodeEnv = process.env.NODE_ENV || 'development'
+
+let databaseUrl
+
+switch (nodeEnv) {
+	case 'production':
+		databaseUrl = process?.env?.PROD_DATABASE_URL || process.env.DEV_DATABASE_URL
+		break
+	case 'test':
+		databaseUrl = process?.env?.TEST_DATABASE_URL || process.env.DEV_DATABASE_URL
+		break
+	default:
+		databaseUrl = process.env.DEV_DATABASE_URL
+}
+
+console.info('Database selected: ', databaseUrl.split('/').at(-1))
+
+// Function to prompt user and get input
+function promptUser(question) {
+	return new Promise((resolve) => {
+		process.stdout.write(question)
+		process.stdin.once('data', (data) => {
+			resolve(data.toString().trim())
+		})
+	})
+}
+// Initialize Sequelize
+const sequelize = new Sequelize(databaseUrl, {
+	dialect: 'postgres',
+	logging: process.env.NODE_ENV === 'development' ? console.log : false,
+})
+
+;(async () => {
+	try {
+		const organizationCode = await promptUser(
+			'---------> Please enter the organization code to delete (e.g., sot): '
+		)
+
+		// Test database connection
+		await sequelize.authenticate()
+		console.log('Database connection established successfully.')
+		REPORT_QUERY = `WITH user_org_roles AS (
+    SELECT 'user_organization_roles' AS table_name, 
+           json_agg(row_to_json(uor)) AS data, 
+           count(*) AS row_count
+    FROM user_organization_roles uor
+    WHERE organization_code = '${organizationCode}'
+),
+user_orgs AS (
+    SELECT 'user_organizations' AS table_name, 
+           json_agg(row_to_json(uo)) AS data, 
+           count(*) AS row_count
+    FROM user_organizations uo
+    WHERE organization_code = '${organizationCode}'
+),
+users_to_delete AS (
+    SELECT 'users' AS table_name, 
+           json_agg(row_to_json(u)) AS data, 
+           count(*) AS row_count
+    FROM users u
+    WHERE u.id IN (
+        SELECT user_id 
+        FROM user_organizations 
+        WHERE organization_code = '${organizationCode}'
+    )
+),
+orgs AS (
+    SELECT 'organizations' AS table_name, 
+           json_agg(row_to_json(o)) AS data, 
+           count(*) AS row_count
+    FROM organizations o
+    WHERE code = '${organizationCode}'
+),
+invites AS (
+    SELECT 'organization_user_invites' AS table_name, 
+           json_agg(row_to_json(oui)) AS data, 
+           count(*) AS row_count
+    FROM organization_user_invites oui
+    WHERE organization_code = '${organizationCode}'
+),
+notifications AS (
+    SELECT 'notification_templates' AS table_name, 
+           json_agg(row_to_json(nt)) AS data, 
+           count(*) AS row_count
+    FROM notification_templates nt
+    WHERE organization_code = '${organizationCode}'
+),
+features AS (
+    SELECT 'organization_features' AS table_name, 
+           json_agg(row_to_json(of)) AS data, 
+           count(*) AS row_count
+    FROM organization_features of
+    WHERE organization_code = '${organizationCode}'
+),
+reg_codes AS (
+    SELECT 'organization_registration_codes' AS table_name, 
+           json_agg(row_to_json(orc)) AS data, 
+           count(*) AS row_count
+    FROM organization_registration_codes orc
+    WHERE organization_code = '${organizationCode}'
+)
+SELECT table_name, row_count
+FROM user_org_roles
+UNION ALL
+SELECT table_name, row_count
+FROM user_orgs
+UNION ALL
+SELECT table_name, row_count
+FROM users_to_delete
+UNION ALL
+SELECT table_name, row_count
+FROM orgs
+UNION ALL
+SELECT table_name, row_count
+FROM invites
+UNION ALL
+SELECT table_name, row_count
+FROM notifications
+UNION ALL
+SELECT table_name, row_count
+FROM features
+UNION ALL
+SELECT table_name, row_count
+FROM reg_codes;`
+		const report = await sequelize.query(REPORT_QUERY, {
+			type: Sequelize.QueryTypes.RAW,
+			raw: true,
+		})
+
+		let table = new Table({
+			head: ['Table Name', 'Row Count'],
+			colWidths: [30, 15],
+		})
+
+		// Populate the table with data
+		report[0].forEach((item) => {
+			table.push([item.table_name, item.row_count])
+		})
+
+		// Print the table
+		console.log('\n\n\nBelow is number of rows attached to the organization code: ', organizationCode)
+		console.log(table.toString())
+
+		const confirmation = await promptUser(
+			'---------> Are you sure you want to delete all data related to this organization? (yes/no): '
+		)
+		// Start transaction
+		if (confirmation.toLowerCase() == 'yes') {
+			await sequelize.transaction(async (t) => {
+				// Delete from user_organization_roles
+				const DELETE_USER_ORG_ROLES_QUERY =
+					'DELETE FROM user_organization_roles WHERE organization_code = :organizationCode RETURNING *'
+				const deletedUserOrgRoles = await sequelize.query(DELETE_USER_ORG_ROLES_QUERY, {
+					replacements: { organizationCode },
+					type: Sequelize.QueryTypes.DELETE,
+					raw: true,
+					transaction: t,
+				})
+				console.info('Deleted user_organization_roles: ', deletedUserOrgRoles)
+
+				// Delete from user_organizations and capture user_ids
+				const DELETE_USER_ORGS_QUERY =
+					'DELETE FROM user_organizations WHERE organization_code = :organizationCode RETURNING user_id'
+				const deletedUserOrgs = await sequelize.query(DELETE_USER_ORGS_QUERY, {
+					replacements: { organizationCode },
+					type: Sequelize.QueryTypes.DELETE,
+					raw: true,
+					transaction: t,
+				})
+				const userIdsToDelete = deletedUserOrgs.map((row) => row.user_id)
+				console.info('Deleted user_organizations, user_ids: ', userIdsToDelete)
+
+				// Delete from users
+				if (userIdsToDelete.length > 0) {
+					const DELETE_USERS_QUERY =
+						'DELETE FROM users WHERE id IN (:userIdsToDelete) AND NOT EXISTS (SELECT 1 FROM user_organizations WHERE user_organizations.user_id = users.id )  RETURNING *'
+					const deletedUsers = await sequelize.query(DELETE_USERS_QUERY, {
+						replacements: { userIdsToDelete },
+						type: Sequelize.QueryTypes.DELETE,
+						raw: true,
+						transaction: t,
+					})
+					console.info('Deleted users: ', deletedUsers)
+				}
+
+				// Delete from organizations
+				const DELETE_ORGS_QUERY = 'DELETE FROM organizations WHERE code = :organizationCode RETURNING *'
+				const deletedOrgs = await sequelize.query(DELETE_ORGS_QUERY, {
+					replacements: { organizationCode },
+					type: Sequelize.QueryTypes.DELETE,
+					raw: true,
+					transaction: t,
+				})
+				console.info('Deleted organizations: ', deletedOrgs)
+
+				// Delete from organization_user_invites
+				const DELETE_INVITES_QUERY =
+					'DELETE FROM organization_user_invites WHERE organization_code = :organizationCode RETURNING *'
+				const deletedInvites = await sequelize.query(DELETE_INVITES_QUERY, {
+					replacements: { organizationCode },
+					type: Sequelize.QueryTypes.DELETE,
+					raw: true,
+					transaction: t,
+				})
+				console.info('Deleted organization_user_invites: ', deletedInvites)
+
+				// Delete from notification_templates
+				const DELETE_NOTIFICATIONS_QUERY =
+					'DELETE FROM notification_templates WHERE organization_code = :organizationCode RETURNING *'
+				const deletedNotifications = await sequelize.query(DELETE_NOTIFICATIONS_QUERY, {
+					replacements: { organizationCode },
+					type: Sequelize.QueryTypes.DELETE,
+					raw: true,
+					transaction: t,
+				})
+				console.info('Deleted notification_templates: ', deletedNotifications)
+
+				// Delete from organization_features
+				const DELETE_FEATURES_QUERY =
+					'DELETE FROM organization_features WHERE organization_code = :organizationCode RETURNING *'
+				const deletedFeatures = await sequelize.query(DELETE_FEATURES_QUERY, {
+					replacements: { organizationCode },
+					type: Sequelize.QueryTypes.DELETE,
+					raw: true,
+					transaction: t,
+				})
+				console.info('Deleted organization_features: ', deletedFeatures)
+
+				// Delete from organization_registration_codes
+				const DELETE_REG_CODES_QUERY =
+					'DELETE FROM organization_registration_codes WHERE organization_code = :organizationCode RETURNING *'
+				const deletedRegCodes = await sequelize.query(DELETE_REG_CODES_QUERY, {
+					replacements: { organizationCode },
+					type: Sequelize.QueryTypes.DELETE,
+					raw: true,
+					transaction: t,
+				})
+				console.info('Deleted organization_registration_codes: ', deletedRegCodes)
+				// await t.commit();
+			})
+
+			console.log('Transaction completed successfully.')
+		} else {
+			console.log('Transaction aborted by user.')
+		}
+	} catch (error) {
+		console.error(`Error executing transaction: ${error}`)
+	} finally {
+		sequelize.close()
+	}
+})()

From 7f101d7d52ffb3e8c462710b54b63e846ee3296c Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Fri, 1 Aug 2025 18:10:31 +0530
Subject: [PATCH 27/55] added Script to hard - delete organization and related
 data

---
 src/scripts/deleted-org-data-clean-up/clean.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/scripts/deleted-org-data-clean-up/clean.js b/src/scripts/deleted-org-data-clean-up/clean.js
index 3a576e6f0..6b07d74e4 100644
--- a/src/scripts/deleted-org-data-clean-up/clean.js
+++ b/src/scripts/deleted-org-data-clean-up/clean.js
@@ -252,5 +252,6 @@ FROM reg_codes;`
 		console.error(`Error executing transaction: ${error}`)
 	} finally {
 		sequelize.close()
+		process.exit()
 	}
 })()

From 79287026f86d0a4a0400052b354a82b9cd15be59 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Fri, 1 Aug 2025 18:35:26 +0530
Subject: [PATCH 28/55] minod changes

---
 .../deleted-org-data-clean-up/clean.js        | 26 ++++++++++++++-----
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/src/scripts/deleted-org-data-clean-up/clean.js b/src/scripts/deleted-org-data-clean-up/clean.js
index 6b07d74e4..02b28f7d7 100644
--- a/src/scripts/deleted-org-data-clean-up/clean.js
+++ b/src/scripts/deleted-org-data-clean-up/clean.js
@@ -18,8 +18,6 @@ switch (nodeEnv) {
 		databaseUrl = process.env.DEV_DATABASE_URL
 }
 
-console.info('Database selected: ', databaseUrl.split('/').at(-1))
-
 // Function to prompt user and get input
 function promptUser(question) {
 	return new Promise((resolve) => {
@@ -40,6 +38,16 @@ const sequelize = new Sequelize(databaseUrl, {
 		const organizationCode = await promptUser(
 			'---------> Please enter the organization code to delete (e.g., sot): '
 		)
+		if (!organizationCode || organizationCode.trim() === '') {
+			console.error('Error: Organization code cannot be empty')
+			process.exit(1)
+		}
+
+		// Validate format (alphanumeric and underscores only)
+		if (!/^[a-z0-9_]+$/.test(organizationCode)) {
+			console.error('Error: Organization code must contain only lowercase letters, numbers, and underscores')
+			process.exit(1)
+		}
 
 		// Test database connection
 		await sequelize.authenticate()
@@ -136,9 +144,10 @@ FROM reg_codes;`
 			head: ['Table Name', 'Row Count'],
 			colWidths: [30, 15],
 		})
-
+		let countChecker = 0
 		// Populate the table with data
 		report[0].forEach((item) => {
+			countChecker += item.row_count
 			table.push([item.table_name, item.row_count])
 		})
 
@@ -146,9 +155,12 @@ FROM reg_codes;`
 		console.log('\n\n\nBelow is number of rows attached to the organization code: ', organizationCode)
 		console.log(table.toString())
 
-		const confirmation = await promptUser(
-			'---------> Are you sure you want to delete all data related to this organization? (yes/no): '
-		)
+		const confirmation =
+			countChecker > 0
+				? await promptUser(
+						'---------> Are you sure you want to delete all data related to this organization? (yes/no): '
+				  )
+				: 'no'
 		// Start transaction
 		if (confirmation.toLowerCase() == 'yes') {
 			await sequelize.transaction(async (t) => {
@@ -246,7 +258,7 @@ FROM reg_codes;`
 
 			console.log('Transaction completed successfully.')
 		} else {
-			console.log('Transaction aborted by user.')
+			console.log(`Transaction aborted${countChecker == 0 ? '! No records to delete.' : ' by the user'}  `)
 		}
 	} catch (error) {
 		console.error(`Error executing transaction: ${error}`)

From 6e61a4b7ea8380dde032d08d276cf04289cecd1f Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Mon, 4 Aug 2025 12:48:29 +0530
Subject: [PATCH 29/55] refactor: AdminHelper.create for improved admin user
 creation workflow

---
 src/services/admin.js | 182 ++++++++++++++++++++++++++++++++----------
 1 file changed, 138 insertions(+), 44 deletions(-)

diff --git a/src/services/admin.js b/src/services/admin.js
index ee6a2756e..93c558bec 100644
--- a/src/services/admin.js
+++ b/src/services/admin.js
@@ -6,27 +6,38 @@
  */
 
 // Dependencies
+// Third-party libraries
+const _ = require('lodash')
+const { Op } = require('sequelize')
 
+// Constants and generics
 const common = require('@constants/common')
 const httpStatusCode = require('@generics/http-status')
 const utils = require('@generics/utils')
-const _ = require('lodash')
-const userQueries = require('@database/queries/users')
-const roleQueries = require('@database/queries/user-role')
+
+// Database queries
 const organizationQueries = require('@database/queries/organization')
+const roleQueries = require('@database/queries/user-role')
+const tenantQueries = require('@database/queries/tenants')
+const UserCredentialQueries = require('@database/queries/userCredential')
 const userOrganizationQueries = require('@database/queries/userOrganization')
 const userOrganizationRoleQueries = require('@database/queries/userOrganizationRole')
-const { eventBroadcaster } = require('@helpers/eventBroadcaster')
-const { Op } = require('sequelize')
-const Sequelize = require('@database/models/index').sequelize
-const UserCredentialQueries = require('@database/queries/userCredential')
+const userQueries = require('@database/queries/users')
+
+// Services
 const adminService = require('../generics/materializedViews')
+const userSessionsService = require('@services/user-sessions')
+
+// Helpers and utilities
+const { broadcastUserEvent } = require('@helpers/eventBroadcasterMain')
 const emailEncryption = require('@utils/emailEncryption')
+const { eventBroadcaster } = require('@helpers/eventBroadcaster')
+const { generateUniqueUsername } = require('@utils/usernameGenerator')
 const responses = require('@helpers/responses')
-const userSessionsService = require('@services/user-sessions')
 const userHelper = require('@helpers/userHelper')
+
+// DTOs
 const UserTransformDTO = require('@dtos/userDTO')
-const { broadcastUserEvent } = require('@helpers/eventBroadcasterMain')
 
 module.exports = class AdminHelper {
 	/**
@@ -78,68 +89,151 @@ module.exports = class AdminHelper {
 	}
 
 	/**
-	 * create admin users
+	 * Creates a new admin user.
+	 *
+	 * @async
 	 * @method
 	 * @name create
-	 * @param {Object} bodyData - user create information
-	 * @param {string} bodyData.email - email.
-	 * @param {string} bodyData.password - email.
-	 * @returns {JSON} - returns created user information
+	 * @param {Object} bodyData - Data for creating the user.
+	 * @param {string} bodyData.email - Email of the user.
+	 * @param {string} bodyData.password - Password for the user account.
+	 * @param {string} [bodyData.username] - Optional username (generated if not provided).
+	 * @param {string} [bodyData.name] - Optional name (used for username generation if username not provided).
+	 * @returns {Promise<Object>} - Response containing the created user data or error.
+	 *
+	 * @throws {Error} If default tenant or organization is not found or if creation fails.
 	 */
+
 	static async create(bodyData) {
 		try {
+			// Handle email encryption
 			const plaintextEmailId = bodyData.email.toLowerCase()
 			const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId)
-			const user = await UserCredentialQueries.findOne({ email: encryptedEmailId })
 
-			if (user) {
+			// Get default tenant details
+			const tenantDetail = await tenantQueries.findOne({
+				code: process.env.DEFAULT_TENANT_CODE,
+				status: common.ACTIVE_STATUS,
+			})
+
+			if (!tenantDetail) {
 				return responses.failureResponse({
-					message: 'ADMIN_USER_ALREADY_EXISTS',
+					message: 'DEFAULT_TENANT_NOT_FOUND',
 					statusCode: httpStatusCode.not_acceptable,
 					responseCode: 'CLIENT_ERROR',
 				})
 			}
 
-			let role = await roleQueries.findOne({ title: common.ADMIN_ROLE })
-			if (!role) {
+			// Check if admin user already exists
+			const existingUser = await userQueries.findOne(
+				{
+					email: encryptedEmailId,
+					password: { [Op.ne]: null },
+					tenant_code: tenantDetail.code,
+				},
+				{
+					attributes: ['id'],
+				}
+			)
+
+			if (existingUser) {
 				return responses.failureResponse({
-					message: 'ROLE_NOT_FOUND',
+					message: 'ADMIN_USER_ALREADY_EXISTS',
 					statusCode: httpStatusCode.not_acceptable,
 					responseCode: 'CLIENT_ERROR',
 				})
 			}
 
-			bodyData.roles = [role.id]
-			bodyData.organization_id = process.env.DEFAULT_ORG_ID
-			bodyData.tenant_code = process.env.DEFAULT_TENANT_CODE
-
-			// if (!bodyData.organization_id) {
-			// 	let organization = await organizationQueries.findOne(
-			// 		{ code: process.env.DEFAULT_ORGANISATION_CODE },
-			// 		{ attributes: ['id'] }
-			// 	)
-			// 	bodyData.organization_id = organization.id
-			// }
-			bodyData.password = utils.hashPassword(bodyData.password)
-			bodyData.email = encryptedEmailId
-			const createdUser = await userQueries.create(bodyData)
-			const userCredentialsBody = {
-				email: bodyData.email,
-				password: bodyData.password,
-				organization_id: process.env.DEFAULT_ORG_ID,
-				user_id: createdUser.id,
-			}
-			const userData = await UserCredentialQueries.create(userCredentialsBody)
-			if (!userData?.id) {
+			// Find admin role
+			const role = await roleQueries.findOne({
+				title: common.ADMIN_ROLE,
+				tenant_code: tenantDetail.code,
+			})
+
+			if (!role) {
 				return responses.failureResponse({
-					message: userData,
+					message: 'ADMIN_ROLE_NOT_FOUND',
 					statusCode: httpStatusCode.not_acceptable,
 					responseCode: 'CLIENT_ERROR',
 				})
 			}
+
+			// Get default organization code
+			const defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
+			if (!defaultOrganizationCode) {
+				throw new Error('Default organization code not found.')
+			}
+
+			// Prepare user data
+			bodyData.email = encryptedEmailId
+			bodyData.password = utils.hashPassword(bodyData.password)
+			bodyData.tenant_code = tenantDetail.code
+			bodyData.roles = [role.id]
+
+			// Generate username if not provided
+			if (!bodyData.username) {
+				bodyData.username = await generateUniqueUsername(bodyData.name)
+			}
+
+			// Create user
+			const createdUser = await userQueries.create(bodyData)
+
+			// Create user-organization relationship
+			await userOrganizationQueries.create({
+				user_id: createdUser.id,
+				organization_code: defaultOrganizationCode,
+				tenant_code: tenantDetail.code,
+			})
+
+			// Create user-organization-role relationship
+			await userOrganizationRoleQueries.create({
+				tenant_code: tenantDetail.code,
+				user_id: createdUser.id,
+				organization_code: defaultOrganizationCode,
+				role_id: role.id,
+			})
+
+			// Get created user with organization details
+			const user = await userQueries.findUserWithOrganization(
+				{ id: createdUser.id, tenant_code: tenantDetail.code },
+				{
+					attributes: {
+						exclude: ['password'],
+					},
+				}
+			)
+
+			// Process response data
+			const processedUser = user
+			processedUser.email = plaintextEmailId
+
+			// Emit user creation event
+			const eventBody = UserTransformDTO.eventBodyDTO({
+				entity: 'user',
+				eventType: 'create',
+				entityId: processedUser?.id,
+				args: {
+					created_by: processedUser.id,
+					name: processedUser?.name,
+					username: processedUser?.username,
+					email: processedUser.email,
+					phone: processedUser?.phone,
+					organizations: processedUser?.organizations,
+					tenant_code: processedUser?.tenant_code,
+					created_at: processedUser?.created_at || new Date(),
+					updated_at: processedUser?.updated_at || new Date(),
+					status: createdUser?.status || common.ACTIVE_STATUS,
+					deleted: false,
+					id: processedUser.id,
+				},
+			})
+
+			broadcastUserEvent('userEvents', { requestBody: eventBody, isInternal: true })
+
 			return responses.successResponse({
 				statusCode: httpStatusCode.created,
-				message: 'USER_CREATED_SUCCESSFULLY',
+				message: 'ADMIN_USER_CREATED_SUCCESSFULLY',
+				result: { user },
 			})
 		} catch (error) {
 			console.log(error)

From 214a8d90e7627ebfcaa01c844a568664f5fef676 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Mon, 4 Aug 2025 13:03:11 +0530
Subject: [PATCH 30/55] removed: defaultOrganizationCode validation

---
 src/services/admin.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/services/admin.js b/src/services/admin.js
index 93c558bec..d60944bee 100644
--- a/src/services/admin.js
+++ b/src/services/admin.js
@@ -160,9 +160,6 @@ module.exports = class AdminHelper {
 
 			// Get default organization code
 			const defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
-			if (!defaultOrganizationCode) {
-				throw new Error('Default organization code not found.')
-			}
 
 			// Prepare user data
 			bodyData.email = encryptedEmailId

From 911bcccf513b9182f8381990228460f776ccb960 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Mon, 4 Aug 2025 14:09:59 +0530
Subject: [PATCH 31/55] updated: admin create to use transaction

---
 src/services/admin.js | 131 ++++++++++++++++++++----------------------
 1 file changed, 62 insertions(+), 69 deletions(-)

diff --git a/src/services/admin.js b/src/services/admin.js
index d60944bee..d731c8efd 100644
--- a/src/services/admin.js
+++ b/src/services/admin.js
@@ -23,6 +23,7 @@ const UserCredentialQueries = require('@database/queries/userCredential')
 const userOrganizationQueries = require('@database/queries/userOrganization')
 const userOrganizationRoleQueries = require('@database/queries/userOrganizationRole')
 const userQueries = require('@database/queries/users')
+const { sequelize } = require('@database/models/index')
 
 // Services
 const adminService = require('../generics/materializedViews')
@@ -105,8 +106,11 @@ module.exports = class AdminHelper {
 	 */
 
 	static async create(bodyData) {
+		let transaction
+
 		try {
-			// Handle email encryption
+			transaction = await sequelize.transaction()
+
 			const plaintextEmailId = bodyData.email.toLowerCase()
 			const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId)
 
@@ -115,50 +119,27 @@ module.exports = class AdminHelper {
 				code: process.env.DEFAULT_TENANT_CODE,
 				status: common.ACTIVE_STATUS,
 			})
+			if (!tenantDetail) throw new Error('DEFAULT_TENANT_NOT_FOUND')
 
-			if (!tenantDetail) {
-				return responses.failureResponse({
-					message: 'DEFAULT_TENANT_NOT_FOUND',
-					statusCode: httpStatusCode.not_acceptable,
-					responseCode: 'CLIENT_ERROR',
-				})
-			}
-
-			// Check if admin user already exists
 			const existingUser = await userQueries.findOne(
 				{
 					email: encryptedEmailId,
 					password: { [Op.ne]: null },
 					tenant_code: tenantDetail.code,
 				},
-				{
-					attributes: ['id'],
-				}
+				{ attributes: ['id'], transaction }
 			)
+			if (existingUser) throw new Error('ADMIN_USER_ALREADY_EXISTS')
 
-			if (existingUser) {
-				return responses.failureResponse({
-					message: 'ADMIN_USER_ALREADY_EXISTS',
-					statusCode: httpStatusCode.not_acceptable,
-					responseCode: 'CLIENT_ERROR',
-				})
-			}
-
-			// Find admin role
-			const role = await roleQueries.findOne({
-				title: common.ADMIN_ROLE,
-				tenant_code: tenantDetail.code,
-			})
-
-			if (!role) {
-				return responses.failureResponse({
-					message: 'ADMIN_ROLE_NOT_FOUND',
-					statusCode: httpStatusCode.not_acceptable,
-					responseCode: 'CLIENT_ERROR',
-				})
-			}
+			const role = await roleQueries.findOne(
+				{
+					title: common.ADMIN_ROLE,
+					tenant_code: tenantDetail.code,
+				},
+				{ transaction }
+			)
+			if (!role) throw new Error('ADMIN_ROLE_NOT_FOUND')
 
-			// Get default organization code
 			const defaultOrganizationCode = process.env.DEFAULT_ORGANISATION_CODE
 
 			// Prepare user data
@@ -176,52 +157,45 @@ module.exports = class AdminHelper {
 			const createdUser = await userQueries.create(bodyData)
 
 			// Create user-organization relationship
-			await userOrganizationQueries.create({
-				user_id: createdUser.id,
-				organization_code: defaultOrganizationCode,
-				tenant_code: tenantDetail.code,
-			})
+			await userOrganizationQueries.create(
+				{
+					user_id: createdUser.id,
+					organization_code: defaultOrganizationCode,
+					tenant_code: tenantDetail.code,
+				},
+				{ transaction }
+			)
 
 			// Create user-organization-role relationship
-			await userOrganizationRoleQueries.create({
-				tenant_code: tenantDetail.code,
-				user_id: createdUser.id,
-				organization_code: defaultOrganizationCode,
-				role_id: role.id,
-			})
+			await userOrganizationRoleQueries.create(
+				{
+					tenant_code: tenantDetail.code,
+					user_id: createdUser.id,
+					organization_code: defaultOrganizationCode,
+					role_id: role.id,
+				},
+				{ transaction }
+			)
+
+			await transaction.commit()
 
-			// Get created user with organization details
 			const user = await userQueries.findUserWithOrganization(
 				{ id: createdUser.id, tenant_code: tenantDetail.code },
-				{
-					attributes: {
-						exclude: ['password'],
-					},
-				}
+				{ attributes: { exclude: ['password'] } }
 			)
 
-			// Process response data
-			const processedUser = user
-			processedUser.email = plaintextEmailId
+			const processedUser = { ...user, email: plaintextEmailId }
 
-			// Emit user creation event
 			const eventBody = UserTransformDTO.eventBodyDTO({
 				entity: 'user',
 				eventType: 'create',
-				entityId: processedUser?.id,
+				entityId: processedUser.id,
 				args: {
+					...processedUser,
 					created_by: processedUser.id,
-					name: processedUser?.name,
-					username: processedUser?.username,
-					email: processedUser.email,
-					phone: processedUser?.phone,
-					organizations: processedUser?.organizations,
-					tenant_code: processedUser?.tenant_code,
+					deleted: false,
 					created_at: processedUser?.created_at || new Date(),
 					updated_at: processedUser?.updated_at || new Date(),
-					status: createdUser?.status || common.ACTIVE_STATUS,
-					deleted: false,
-					id: processedUser.id,
 				},
 			})
 
@@ -230,11 +204,30 @@ module.exports = class AdminHelper {
 			return responses.successResponse({
 				statusCode: httpStatusCode.created,
 				message: 'ADMIN_USER_CREATED_SUCCESSFULLY',
-				result: { user },
+				result: { user: processedUser },
 			})
 		} catch (error) {
-			console.log(error)
-			throw error
+			if (transaction && !transaction.finished) {
+				try {
+					await transaction.rollback()
+				} catch (rollbackErr) {
+					console.error('Rollback failed:', rollbackErr)
+				}
+			}
+
+			switch (error.message) {
+				case 'DEFAULT_TENANT_NOT_FOUND':
+				case 'ADMIN_USER_ALREADY_EXISTS':
+				case 'ADMIN_ROLE_NOT_FOUND':
+					return responses.failureResponse({
+						message: error.message,
+						statusCode: httpStatusCode.not_acceptable,
+						responseCode: 'CLIENT_ERROR',
+					})
+				default:
+					console.error('Unexpected error in admin.create:', error)
+					throw error
+			}
 		}
 	}
 

From 2a8b00ed040f794f546de3f02c9e93ad418dee63 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Mon, 4 Aug 2025 14:10:48 +0530
Subject: [PATCH 32/55] added: options to create queries

---
 src/database/queries/userOrganization.js     | 8 ++++----
 src/database/queries/userOrganizationRole.js | 6 +++---
 src/database/queries/users.js                | 7 ++++---
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/database/queries/userOrganization.js b/src/database/queries/userOrganization.js
index a7c0fe9f8..67c48a8c9 100644
--- a/src/database/queries/userOrganization.js
+++ b/src/database/queries/userOrganization.js
@@ -1,17 +1,17 @@
 'use strict'
 
 const { UserOrganization, Organization, UserOrganizationRole, sequelize } = require('@database/models/index')
-const { Op } = require('sequelize')
 
-exports.create = async (data) => {
+exports.create = async (data, options = {}) => {
 	try {
-		const createdUserOrg = await UserOrganization.create(data)
+		const createdUserOrg = await UserOrganization.create(data, options)
 		return createdUserOrg.get({ plain: true })
 	} catch (error) {
 		console.error(error)
-		return error
+		throw error
 	}
 }
+
 exports.changeUserOrganization = async ({ userId, tenantCode, oldOrgCode, newOrgCode }) => {
 	const transaction = await sequelize.transaction()
 
diff --git a/src/database/queries/userOrganizationRole.js b/src/database/queries/userOrganizationRole.js
index 9263b9ea3..f13290fa0 100644
--- a/src/database/queries/userOrganizationRole.js
+++ b/src/database/queries/userOrganizationRole.js
@@ -6,13 +6,13 @@ const { Op } = require('sequelize')
  * Create a new UserOrganizationRole record
  * @param {Object} data - Data for creation
  */
-exports.create = async (data) => {
+exports.create = async (data, options = {}) => {
 	try {
-		const result = await UserOrganizationRole.create(data)
+		const result = await UserOrganizationRole.create(data, options)
 		return result.get({ plain: true })
 	} catch (error) {
 		console.error('Create Error:', error)
-		return error
+		throw error
 	}
 }
 
diff --git a/src/database/queries/users.js b/src/database/queries/users.js
index 0aacefbaa..718115865 100644
--- a/src/database/queries/users.js
+++ b/src/database/queries/users.js
@@ -21,11 +21,12 @@ exports.getModelName = async () => {
 		throw error
 	}
 }
-exports.create = async (data) => {
+
+exports.create = async (data, options = {}) => {
 	try {
-		return await database.User.create(data)
+		return await database.User.create(data, options)
 	} catch (error) {
-		console.log(error)
+		console.error(error)
 		throw error
 	}
 }

From 52b1cfdaafbbc612509a05919bf43398e1772a80 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Mon, 4 Aug 2025 14:35:11 +0530
Subject: [PATCH 33/55] minor fixes

---
 .../deleted-org-data-clean-up/clean.js        | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/scripts/deleted-org-data-clean-up/clean.js b/src/scripts/deleted-org-data-clean-up/clean.js
index 02b28f7d7..ceaef7f3c 100644
--- a/src/scripts/deleted-org-data-clean-up/clean.js
+++ b/src/scripts/deleted-org-data-clean-up/clean.js
@@ -42,10 +42,9 @@ const sequelize = new Sequelize(databaseUrl, {
 			console.error('Error: Organization code cannot be empty')
 			process.exit(1)
 		}
-
-		// Validate format (alphanumeric and underscores only)
-		if (!/^[a-z0-9_]+$/.test(organizationCode)) {
-			console.error('Error: Organization code must contain only lowercase letters, numbers, and underscores')
+		const tenantCode = await promptUser('---------> Please enter the tenant code to delete (e.g., shikshalokam): ')
+		if (!tenantCode || tenantCode.trim() === '') {
+			console.error('Error: tenant code cannot be empty')
 			process.exit(1)
 		}
 
@@ -57,14 +56,14 @@ const sequelize = new Sequelize(databaseUrl, {
            json_agg(row_to_json(uor)) AS data, 
            count(*) AS row_count
     FROM user_organization_roles uor
-    WHERE organization_code = '${organizationCode}'
+    WHERE organization_code = '${organizationCode}' AND tenant_code = '${tenantCode}'
 ),
 user_orgs AS (
     SELECT 'user_organizations' AS table_name, 
            json_agg(row_to_json(uo)) AS data, 
            count(*) AS row_count
     FROM user_organizations uo
-    WHERE organization_code = '${organizationCode}'
+    WHERE organization_code = '${organizationCode}' AND tenant_code = '${tenantCode}'
 ),
 users_to_delete AS (
     SELECT 'users' AS table_name, 
@@ -74,7 +73,7 @@ users_to_delete AS (
     WHERE u.id IN (
         SELECT user_id 
         FROM user_organizations 
-        WHERE organization_code = '${organizationCode}'
+        WHERE organization_code = '${organizationCode}' AND tenant_code = '${tenantCode}'
     )
 ),
 orgs AS (
@@ -82,35 +81,35 @@ orgs AS (
            json_agg(row_to_json(o)) AS data, 
            count(*) AS row_count
     FROM organizations o
-    WHERE code = '${organizationCode}'
+    WHERE code = '${organizationCode}' AND tenant_code = '${tenantCode}'
 ),
 invites AS (
     SELECT 'organization_user_invites' AS table_name, 
            json_agg(row_to_json(oui)) AS data, 
            count(*) AS row_count
     FROM organization_user_invites oui
-    WHERE organization_code = '${organizationCode}'
+    WHERE organization_code = '${organizationCode}' AND tenant_code = '${tenantCode}'
 ),
 notifications AS (
     SELECT 'notification_templates' AS table_name, 
            json_agg(row_to_json(nt)) AS data, 
            count(*) AS row_count
     FROM notification_templates nt
-    WHERE organization_code = '${organizationCode}'
+    WHERE organization_code = '${organizationCode}' AND tenant_code = '${tenantCode}'
 ),
 features AS (
     SELECT 'organization_features' AS table_name, 
            json_agg(row_to_json(of)) AS data, 
            count(*) AS row_count
     FROM organization_features of
-    WHERE organization_code = '${organizationCode}'
+    WHERE organization_code = '${organizationCode}' AND tenant_code = '${tenantCode}'
 ),
 reg_codes AS (
     SELECT 'organization_registration_codes' AS table_name, 
            json_agg(row_to_json(orc)) AS data, 
            count(*) AS row_count
     FROM organization_registration_codes orc
-    WHERE organization_code = '${organizationCode}'
+    WHERE organization_code = '${organizationCode}' AND tenant_code = '${tenantCode}'
 )
 SELECT table_name, row_count
 FROM user_org_roles

From 0a967392180c4349fffd0d4084af65cd4d4727e0 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Mon, 4 Aug 2025 15:07:04 +0530
Subject: [PATCH 34/55] fixed: inconsistent error handling patterns across
 functions

---
 src/database/queries/userOrganization.js     | 6 +++---
 src/database/queries/userOrganizationRole.js | 6 +++---
 src/services/admin.js                        | 2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/database/queries/userOrganization.js b/src/database/queries/userOrganization.js
index 67c48a8c9..1904c662c 100644
--- a/src/database/queries/userOrganization.js
+++ b/src/database/queries/userOrganization.js
@@ -100,7 +100,7 @@ exports.findOne = async (filter, options = {}) => {
 		})
 	} catch (error) {
 		console.error(error)
-		return error
+		throw error
 	}
 }
 
@@ -124,7 +124,7 @@ exports.findAll = async (filter = {}, options = {}) => {
 		})
 	} catch (error) {
 		console.error(error)
-		return error
+		throw error
 	}
 }
 
@@ -140,7 +140,7 @@ exports.update = async (filter, updates) => {
 	} catch (error) {
 		await transaction.rollback()
 		console.error(error)
-		return error
+		throw error
 	}
 }
 
diff --git a/src/database/queries/userOrganizationRole.js b/src/database/queries/userOrganizationRole.js
index f13290fa0..7ec270467 100644
--- a/src/database/queries/userOrganizationRole.js
+++ b/src/database/queries/userOrganizationRole.js
@@ -30,7 +30,7 @@ exports.findOne = async (filter, options = {}) => {
 		})
 	} catch (error) {
 		console.error('FindOne Error:', error)
-		return error
+		throw error
 	}
 }
 
@@ -48,7 +48,7 @@ exports.findAll = async (filter, options = {}) => {
 		})
 	} catch (error) {
 		console.error('FindAll Error:', error)
-		return error
+		throw error
 	}
 }
 
@@ -65,7 +65,7 @@ exports.update = async (values, filter) => {
 		return affectedCount
 	} catch (error) {
 		console.error('Update Error:', error)
-		return error
+		throw error
 	}
 }
 
diff --git a/src/services/admin.js b/src/services/admin.js
index d731c8efd..b088777ee 100644
--- a/src/services/admin.js
+++ b/src/services/admin.js
@@ -154,7 +154,7 @@ module.exports = class AdminHelper {
 			}
 
 			// Create user
-			const createdUser = await userQueries.create(bodyData)
+			const createdUser = await userQueries.create(bodyData, { transaction })
 
 			// Create user-organization relationship
 			await userOrganizationQueries.create(

From 7a3d85d9f5b2ce4d7530f91b298a1026389f9425 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Mon, 4 Aug 2025 15:47:54 +0530
Subject: [PATCH 35/55] minor fixes

---
 .../deleted-org-data-clean-up/clean.js        | 29 ++++++++++---------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/src/scripts/deleted-org-data-clean-up/clean.js b/src/scripts/deleted-org-data-clean-up/clean.js
index ceaef7f3c..dcf339aaf 100644
--- a/src/scripts/deleted-org-data-clean-up/clean.js
+++ b/src/scripts/deleted-org-data-clean-up/clean.js
@@ -165,9 +165,9 @@ FROM reg_codes;`
 			await sequelize.transaction(async (t) => {
 				// Delete from user_organization_roles
 				const DELETE_USER_ORG_ROLES_QUERY =
-					'DELETE FROM user_organization_roles WHERE organization_code = :organizationCode RETURNING *'
+					'DELETE FROM user_organization_roles WHERE organization_code = :organizationCode AND tenant_code = :tenantCode RETURNING *'
 				const deletedUserOrgRoles = await sequelize.query(DELETE_USER_ORG_ROLES_QUERY, {
-					replacements: { organizationCode },
+					replacements: { organizationCode, tenantCode },
 					type: Sequelize.QueryTypes.DELETE,
 					raw: true,
 					transaction: t,
@@ -176,9 +176,9 @@ FROM reg_codes;`
 
 				// Delete from user_organizations and capture user_ids
 				const DELETE_USER_ORGS_QUERY =
-					'DELETE FROM user_organizations WHERE organization_code = :organizationCode RETURNING user_id'
+					'DELETE FROM user_organizations WHERE organization_code = :organizationCode AND tenant_code = :tenantCode RETURNING user_id'
 				const deletedUserOrgs = await sequelize.query(DELETE_USER_ORGS_QUERY, {
-					replacements: { organizationCode },
+					replacements: { organizationCode, tenantCode },
 					type: Sequelize.QueryTypes.DELETE,
 					raw: true,
 					transaction: t,
@@ -200,9 +200,10 @@ FROM reg_codes;`
 				}
 
 				// Delete from organizations
-				const DELETE_ORGS_QUERY = 'DELETE FROM organizations WHERE code = :organizationCode RETURNING *'
+				const DELETE_ORGS_QUERY =
+					'DELETE FROM organizations WHERE code = :organizationCode AND tenant_code = :tenantCode RETURNING *'
 				const deletedOrgs = await sequelize.query(DELETE_ORGS_QUERY, {
-					replacements: { organizationCode },
+					replacements: { organizationCode, tenantCode },
 					type: Sequelize.QueryTypes.DELETE,
 					raw: true,
 					transaction: t,
@@ -211,9 +212,9 @@ FROM reg_codes;`
 
 				// Delete from organization_user_invites
 				const DELETE_INVITES_QUERY =
-					'DELETE FROM organization_user_invites WHERE organization_code = :organizationCode RETURNING *'
+					'DELETE FROM organization_user_invites WHERE organization_code = :organizationCode AND tenant_code = :tenantCode RETURNING *'
 				const deletedInvites = await sequelize.query(DELETE_INVITES_QUERY, {
-					replacements: { organizationCode },
+					replacements: { organizationCode, tenantCode },
 					type: Sequelize.QueryTypes.DELETE,
 					raw: true,
 					transaction: t,
@@ -222,9 +223,9 @@ FROM reg_codes;`
 
 				// Delete from notification_templates
 				const DELETE_NOTIFICATIONS_QUERY =
-					'DELETE FROM notification_templates WHERE organization_code = :organizationCode RETURNING *'
+					'DELETE FROM notification_templates WHERE organization_code = :organizationCode AND tenant_code = :tenantCode RETURNING *'
 				const deletedNotifications = await sequelize.query(DELETE_NOTIFICATIONS_QUERY, {
-					replacements: { organizationCode },
+					replacements: { organizationCode, tenantCode },
 					type: Sequelize.QueryTypes.DELETE,
 					raw: true,
 					transaction: t,
@@ -233,9 +234,9 @@ FROM reg_codes;`
 
 				// Delete from organization_features
 				const DELETE_FEATURES_QUERY =
-					'DELETE FROM organization_features WHERE organization_code = :organizationCode RETURNING *'
+					'DELETE FROM organization_features WHERE organization_code = :organizationCode AND tenant_code = :tenantCode RETURNING *'
 				const deletedFeatures = await sequelize.query(DELETE_FEATURES_QUERY, {
-					replacements: { organizationCode },
+					replacements: { organizationCode, tenantCode },
 					type: Sequelize.QueryTypes.DELETE,
 					raw: true,
 					transaction: t,
@@ -244,9 +245,9 @@ FROM reg_codes;`
 
 				// Delete from organization_registration_codes
 				const DELETE_REG_CODES_QUERY =
-					'DELETE FROM organization_registration_codes WHERE organization_code = :organizationCode RETURNING *'
+					'DELETE FROM organization_registration_codes WHERE organization_code = :organizationCode AND tenant_code = :tenantCode RETURNING *'
 				const deletedRegCodes = await sequelize.query(DELETE_REG_CODES_QUERY, {
-					replacements: { organizationCode },
+					replacements: { organizationCode, tenantCode },
 					type: Sequelize.QueryTypes.DELETE,
 					raw: true,
 					transaction: t,

From 1306701ee4a5e0331f7e29452222cad7c99b1eb7 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Mon, 4 Aug 2025 16:23:04 +0530
Subject: [PATCH 36/55] updated: acceptTermsAndCondition to work with tenant
 code

---
 src/controllers/v1/account.js |  2 +-
 src/locales/en.json           |  2 +-
 src/services/account.js       | 27 ++++++++++++++++-----------
 3 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/src/controllers/v1/account.js b/src/controllers/v1/account.js
index 37f77552c..ab01de633 100644
--- a/src/controllers/v1/account.js
+++ b/src/controllers/v1/account.js
@@ -184,7 +184,7 @@ module.exports = class Account {
 		try {
 			const result = await accountService.acceptTermsAndCondition(
 				req.decodedToken.id,
-				req.decodedToken.organization_id
+				req.decodedToken.tenant_code
 			)
 			return result
 		} catch (error) {
diff --git a/src/locales/en.json b/src/locales/en.json
index 5a5f4b231..0c44ebe9e 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -2,7 +2,7 @@
 	"EMAIL_INVALID": "Email Id is invalid.",
 	"PASSWORD_INVALID": "Password is invalid.",
 	"USER_ALREADY_EXISTS": "User already exists.",
-	"USER_DOESNOT_EXISTS": "Please enter correct email ID",
+	"USER_DOESNOT_EXISTS": "User does not exist.",
 	"USERNAME_OR_PASSWORD_IS_INVALID": "Please enter the correct login ID and Password.",
 	"EMAIL_ID_NOT_REGISTERED": "Please enter the correct login ID and Password.",
 	"USER_CREATED_SUCCESSFULLY": "Sign-up successful, Please wait while logging in.",
diff --git a/src/services/account.js b/src/services/account.js
index 09bfe915f..a7cdb626a 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -1675,17 +1675,22 @@ module.exports = class AccountHelper {
 	}
 
 	/**
-	 * Accept term and condition
+	 * Accept terms and conditions for a user
 	 * @method
 	 * @name acceptTermsAndCondition
-	 * @param {string} userId - userId.
-	 * @returns {JSON} - returns accept the term success response
+	 * @param {string|number} userId - The ID of the user accepting terms and conditions
+	 * @param {string} tenantCode - The tenant code associated with the user
+	 * @returns {Promise<Object>} Promise that resolves to a JSON response object containing success/failure status
+	 * @throws {Error} Throws error if database operation fails
 	 */
-	static async acceptTermsAndCondition(userId, orgId) {
+	static async acceptTermsAndCondition(userId, tenantCode) {
 		try {
-			const user = await userQueries.findByPk(userId)
+			const [affectedRows] = await userQueries.updateUser(
+				{ id: userId, tenant_code: tenantCode },
+				{ has_accepted_terms_and_conditions: true }
+			)
 
-			if (!user) {
+			if (affectedRows === 0) {
 				return responses.failureResponse({
 					message: 'USER_DOESNOT_EXISTS',
 					statusCode: httpStatusCode.bad_request,
@@ -1693,11 +1698,11 @@ module.exports = class AccountHelper {
 				})
 			}
 
-			await userQueries.updateUser(
-				{ id: userId, organization_id: orgId },
-				{ has_accepted_terms_and_conditions: true }
-			)
-			await utilsHelper.redisDel(common.redisUserPrefix + userId.toString())
+			// Clear Redis cache asynchronously (fire and forget)
+			const redisUserKey = `${common.redisUserPrefix}${tenantCode}_${userId}`
+			utilsHelper.redisDel(redisUserKey).catch((err) => {
+				console.error('Redis delete error:', err)
+			})
 
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,

From 2f4fd2646de6432f48041ba4c0130564f8ab12f8 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Mon, 4 Aug 2025 16:47:52 +0530
Subject: [PATCH 37/55] added: tenant code to generateToken

---
 src/services/account.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/services/account.js b/src/services/account.js
index a7cdb626a..cf4ea4db4 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -911,6 +911,7 @@ module.exports = class AccountHelper {
 			const userSessionData = await userSessionsService.findUserSession(
 				{
 					id: decodedToken.data.session_id,
+					tenant_code: decodedToken.data.tenant_code,
 				},
 				{
 					attributes: ['refresh_token'],

From 32ea386087e7330b537736778a403d9495f8b195 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Mon, 4 Aug 2025 17:37:04 +0530
Subject: [PATCH 38/55] Tenant code filter added to organization/read internal
 api

---
 src/controllers/v1/organization.js | 3 ++-
 src/services/organization.js       | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/controllers/v1/organization.js b/src/controllers/v1/organization.js
index e0453c32f..e56eb6ea5 100644
--- a/src/controllers/v1/organization.js
+++ b/src/controllers/v1/organization.js
@@ -153,7 +153,8 @@ module.exports = class Organization {
 		try {
 			const result = await orgService.read(
 				req.query.organisation_id ? req.query.organisation_id : '',
-				req.query.organisation_code ? req.query.organisation_code : ''
+				req.query.organisation_code ? req.query.organisation_code : '',
+				req.query.tenant_code ? req.query.tenant_code : ''
 			)
 			return result
 		} catch (error) {
diff --git a/src/services/organization.js b/src/services/organization.js
index 643a71b97..377dc0ecf 100644
--- a/src/services/organization.js
+++ b/src/services/organization.js
@@ -399,7 +399,7 @@ module.exports = class OrganizationsHelper {
 	 * @returns {JSON} 									- Organization creation details.
 	 */
 
-	static async read(organisationId, organisationCode) {
+	static async read(organisationId, organisationCode, tenantCode = null) {
 		try {
 			let filter = {}
 			// Build filter based on incoming query
@@ -407,6 +407,7 @@ module.exports = class OrganizationsHelper {
 				filter.id = parseInt(organisationId)
 			} else {
 				filter.code = organisationCode
+				if (tenantCode.trim()) filter.tenant_code = tenantCode
 			}
 
 			const organisationDetails = await organizationQueries.findOne(filter)

From 785daa3d4a06c41fc392477d81514502b563d099 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Wed, 6 Aug 2025 12:23:52 +0530
Subject: [PATCH 39/55] updated: getBulkInvitesFilesList to work with
 tenant_code

---
 src/database/models/fileUpload.js  |  1 -
 src/database/queries/fileUpload.js | 39 +++++++++++-------------------
 src/services/org-admin.js          | 15 +++++++-----
 src/validators/v1/org-admin.js     |  2 +-
 4 files changed, 24 insertions(+), 33 deletions(-)

diff --git a/src/database/models/fileUpload.js b/src/database/models/fileUpload.js
index 4aee7c075..8f1f210fb 100644
--- a/src/database/models/fileUpload.js
+++ b/src/database/models/fileUpload.js
@@ -44,7 +44,6 @@ module.exports = (sequelize, DataTypes) => {
 			tenant_code: {
 				type: DataTypes.STRING,
 				allowNull: false,
-				defaultValue: '',
 			},
 		},
 		{ sequelize, modelName: 'FileUpload', tableName: 'file_uploads', freezeTableName: true, paranoid: true }
diff --git a/src/database/queries/fileUpload.js b/src/database/queries/fileUpload.js
index 624e06136..0ac939a1d 100644
--- a/src/database/queries/fileUpload.js
+++ b/src/database/queries/fileUpload.js
@@ -1,5 +1,5 @@
 'use strict'
-const FileUpload = require('../models/index').FileUpload
+const FileUpload = require('@database/models/index').FileUpload
 
 exports.create = async (data) => {
 	try {
@@ -7,7 +7,7 @@ exports.create = async (data) => {
 		const result = createFileUpload.get({ plain: true })
 		return result
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
@@ -19,7 +19,7 @@ exports.findOne = async (filter, options = {}) => {
 			raw: true,
 		})
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
@@ -33,38 +33,27 @@ exports.update = async (filter, update, options = {}) => {
 
 		return res
 	} catch (error) {
-		return error
+		throw error
 	}
 }
 
-exports.listUploads = async (page, limit, status, organization_id) => {
+exports.listUploads = async ({ page, limit, filter = {} } = {}) => {
 	try {
-		let filterQuery = {
-			where: {},
+		const result = await FileUpload.findAndCountAll({
+			where: filter,
 			attributes: {
 				exclude: ['created_at', 'updated_at', 'deleted_at', 'updated_by'],
 			},
-			offset: parseInt((page - 1) * limit, 10),
-			limit: parseInt(limit, 10),
-		}
-
-		if (organization_id) {
-			filterQuery.where.organization_id = organization_id
-		}
-
-		if (status) {
-			filterQuery.where.status = status
-		}
+			offset: (page - 1) * limit,
+			limit,
+			raw: true,
+		})
 
-		const result = await FileUpload.findAndCountAll(filterQuery)
-		const transformedResult = {
+		return {
 			count: result.count,
-			data: result.rows.map((row) => {
-				return row.get({ plain: true })
-			}),
+			data: result.rows,
 		}
-		return transformedResult
 	} catch (error) {
-		return error
+		throw error
 	}
 }
diff --git a/src/services/org-admin.js b/src/services/org-admin.js
index 48db7685f..2ead572d3 100644
--- a/src/services/org-admin.js
+++ b/src/services/org-admin.js
@@ -191,12 +191,15 @@ module.exports = class OrgAdminHelper {
 	 */
 	static async getBulkInvitesFilesList(req) {
 		try {
-			let listFileUpload = await fileUploadQueries.listUploads(
-				req.pageNo,
-				req.pageSize,
-				req.query.status ? req.query.status : null,
-				req.decodedToken.organization_id
-			)
+			const listFileUpload = await fileUploadQueries.listUploads({
+				page: req.pageNo,
+				limit: req.pageSize,
+				filter: {
+					...(req.query.status && { status: req.query.status }),
+					organization_id: req.decodedToken.organization_id,
+					tenant_code: req.decodedToken.tenant_code,
+				},
+			})
 
 			if (listFileUpload.count > 0) {
 				await Promise.all(
diff --git a/src/validators/v1/org-admin.js b/src/validators/v1/org-admin.js
index cfa062558..52d44e437 100644
--- a/src/validators/v1/org-admin.js
+++ b/src/validators/v1/org-admin.js
@@ -16,7 +16,7 @@ module.exports = {
 		req.checkBody('upload_type')
 			.notEmpty()
 			.withMessage('upload_type is required')
-			.isIn()
+			.isIn(upload_type)
 			.withMessage(`upload_type must be from : ${upload_type.join(',')}`)
 	},
 	getRequestDetails: (req) => {

From fd0833b6cf7078b129eb9eef9acc8a817f471da6 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Wed, 6 Aug 2025 14:34:31 +0530
Subject: [PATCH 40/55] feat: add 'theming' column to organizations and
 'configs' column to users

---
 ...6090006-add-theming-and-configs-columns.js | 20 +++++++++++++++++++
 src/database/models/organization.js           |  4 ++++
 src/database/models/users.js                  |  4 ++++
 3 files changed, 28 insertions(+)
 create mode 100644 src/database/migrations/20250806090006-add-theming-and-configs-columns.js

diff --git a/src/database/migrations/20250806090006-add-theming-and-configs-columns.js b/src/database/migrations/20250806090006-add-theming-and-configs-columns.js
new file mode 100644
index 000000000..15479bacd
--- /dev/null
+++ b/src/database/migrations/20250806090006-add-theming-and-configs-columns.js
@@ -0,0 +1,20 @@
+'use strict'
+
+module.exports = {
+	up: async (queryInterface, Sequelize) => {
+		await queryInterface.addColumn('organizations', 'theming', {
+			type: Sequelize.JSON,
+			allowNull: true,
+		})
+
+		await queryInterface.addColumn('users', 'configs', {
+			type: Sequelize.JSONB,
+			allowNull: true,
+		})
+	},
+
+	down: async (queryInterface, Sequelize) => {
+		await queryInterface.removeColumn('organizations', 'theming')
+		await queryInterface.removeColumn('users', 'configs')
+	},
+}
diff --git a/src/database/models/organization.js b/src/database/models/organization.js
index 3e96f7c9f..fc2140f84 100644
--- a/src/database/models/organization.js
+++ b/src/database/models/organization.js
@@ -38,6 +38,10 @@ module.exports = (sequelize, DataTypes) => {
 			in_domain_visibility: {
 				type: DataTypes.STRING,
 			},
+			theming: {
+				type: DataTypes.JSON,
+				allowNull: true,
+			},
 			tenant_code: {
 				type: DataTypes.STRING,
 				allowNull: false,
diff --git a/src/database/models/users.js b/src/database/models/users.js
index bf19e75b9..a4d612680 100644
--- a/src/database/models/users.js
+++ b/src/database/models/users.js
@@ -62,6 +62,10 @@ module.exports = (sequelize, DataTypes) => {
 				type: DataTypes.STRING,
 				defaultValue: 'en',
 			},
+			configs: {
+				type: DataTypes.JSONB,
+				allowNull: true,
+			},
 			tenant_code: {
 				type: DataTypes.STRING,
 				allowNull: false,

From bbbf733a3ce02ee132b2081a5bd1f93a4809ba87 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Wed, 6 Aug 2025 18:01:05 +0530
Subject: [PATCH 41/55] feat: add tenant code to role apis

---
 src/controllers/v1/user-role.js   |  96 +++++++++++++++---------
 src/database/queries/user-role.js |   4 +-
 src/locales/en.json               |  15 ++--
 src/services/user-role.js         | 117 ++++++++++++++++++------------
 4 files changed, 144 insertions(+), 88 deletions(-)

diff --git a/src/controllers/v1/user-role.js b/src/controllers/v1/user-role.js
index 21da3569a..7bd0dc4ec 100644
--- a/src/controllers/v1/user-role.js
+++ b/src/controllers/v1/user-role.js
@@ -10,23 +10,28 @@ const roleService = require('@services/user-role')
 
 module.exports = class userRole {
 	/**
-	 * Create roles.
+	 * Create a new role.
 	 * @method
 	 * @name create
-	 * @param {Object} req - Request data.
-	 * @param {Object} req.body - Request body contains role creation details.
+	 * @param {Object} req - Request object.
+	 * @param {Object} req.body - Request body containing role details.
 	 * @param {String} req.body.title - Title of the role.
-	 * @param {Integer} req.body.userType - User type of the role.
+	 * @param {Number} req.body.userType - User type for the role.
 	 * @param {String} req.body.status - Role status.
-	 * @param {String} req.body.visibility - Visibility of the role.
-	 * @param {String} req.body.translations - Translation for roles.
-	 * @param {Integer} req.body.organization_id - Organization ID for the role.
-	 * @returns {JSON} - Response contains role creation details.
+	 * @param {String} req.body.visibility - Role visibility.
+	 * @param {String} req.body.translations - Role translations.
+	 * @param {Number} req.body.organization_id - Organization ID.
+	 * @param {Object} req.decodedToken - Authenticated user token.
+	 * @returns {Object} - Created role details.
 	 */
 
 	async create(req) {
 		try {
-			const createRole = await roleService.create(req.body, req.decodedToken.organization_id)
+			const createRole = await roleService.create(
+				req.body,
+				req.decodedToken.organization_id,
+				req.decodedToken.tenant_code
+			)
 			return createRole
 		} catch (error) {
 			return error
@@ -34,23 +39,30 @@ module.exports = class userRole {
 	}
 
 	/**
-	 * Update roles.
+	 * Update a role.
 	 * @method
 	 * @name update
-	 * @param {Object} req - Request data.
-	 * @param {Object} req.body - Request body contains role update details.
+	 * @param {Object} req - Request object.
+	 * @param {Object} req.body - Request body containing updated role details.
 	 * @param {String} req.body.title - Title of the role.
-	 * @param {Integer} req.body.userType - User type of the role.
+	 * @param {Number} req.body.userType - User type for the role.
 	 * @param {String} req.body.status - Role status.
-	 * @param {String} req.body.visibility - Visibility of the role.
-	 * @param {String} req.body.translations - Translation for roles.
-	 * @param {Integer} req.body.organization_id - Organization ID for the role.
-	 * @returns {JSON} - Response contains role update details.
+	 * @param {String} req.body.visibility - Role visibility.
+	 * @param {String} req.body.translations - Role translations.
+	 * @param {Number} req.body.organization_id - Organization ID.
+	 * @param {String} req.params.id - Role ID to be updated.
+	 * @param {Object} req.decodedToken - Authenticated user token.
+	 * @returns {Object} - Updated role details.
 	 */
 
 	async update(req) {
 		try {
-			const updateRole = await roleService.update(req.params.id, req.body, req.decodedToken.organization_id)
+			const updateRole = await roleService.update(
+				req.params.id,
+				req.body,
+				req.decodedToken.organization_id,
+				req.decodedToken.tenant_code
+			)
 			return updateRole
 		} catch (error) {
 			return error
@@ -58,32 +70,39 @@ module.exports = class userRole {
 	}
 
 	/**
-	 * Delete role.
+	 * Delete a role.
 	 * @method
 	 * @name delete
-	 * @param {Object} req - Request data.
-	 * @returns {JSON} - Role deletion response.
+	 * @param {Object} req - Request object.
+	 * @param {String} req.params.id - Role ID to be deleted.
+	 * @param {Object} req.decodedToken - Authenticated user token.
+	 * @returns {Object} - Deletion response.
 	 */
-
 	async delete(req) {
 		try {
-			return await roleService.delete(req.params.id, req.decodedToken.organization_id)
+			return await roleService.delete(
+				req.params.id,
+				req.decodedToken.organization_id,
+				req.decodedToken.tenant_code
+			)
 		} catch (error) {
 			return error
 		}
 	}
 
 	/**
-	 * Get all available roles.
+	 * Get a paginated list of roles with optional filters and search.
 	 * @method
-	 * @name defaultlist
-	 * @param {Array(String)} req.body.filters - Filters.
+	 * @name list
+	 * @param {Object} req - Request object.
+	 * @param {Object} req.query - Query parameters.
+	 * @param {Array<String>} [req.query.filters] - Filters.
+	 * @param {String} [req.query.language] - Language code.
 	 * @param {String} req.pageNo - Page number.
 	 * @param {String} req.pageSize - Page size limit.
 	 * @param {String} req.searchText - Search text.
-	 * @param {Integer} req.decodedToken.organization_id - user organization_id.
-	 * @query {String} req.query.language - Language Code
-	 * @returns {JSON} - Role list.
+	 * @param {Object} req.decodedToken - Authenticated user token.
+	 * @returns {Object} - List of roles.
 	 */
 	async list(req) {
 		try {
@@ -94,7 +113,7 @@ module.exports = class userRole {
 				req.searchText,
 				req.decodedToken.organization_id,
 				req.decodedToken.tenant_code,
-				req.query.language ? req.query.language : ''
+				req.query.language || ''
 			)
 			return roleList
 		} catch (error) {
@@ -103,21 +122,30 @@ module.exports = class userRole {
 	}
 
 	/**
-	 * Get all available roles.
+	 * @deprecated
+	 * This method is deprecated. Use `list()` instead.
 	 * @method
 	 * @name default
-	 * @param {Array(String)} req.body.filters - Filters.
+	 * @param {Object} req - Request object.
+	 * @param {Array<String>} req.body.filters - Filters.
 	 * @param {String} req.pageNo - Page number.
 	 * @param {String} req.pageSize - Page size limit.
 	 * @param {String} req.searchText - Search text.
-	 * @returns {JSON} - Role list.
+	 * @returns {Object} - List of roles.
 	 */
+	/* 
 	async default(req) {
 		try {
-			const defaultRoleList = await roleService.defaultList(req.query, req.pageNo, req.pageSize, req.searchText)
+			const defaultRoleList = await roleService.defaultList(
+				req.query, 
+				req.pageNo, 
+				req.pageSize, 
+				req.searchText
+			)
 			return defaultRoleList
 		} catch (error) {
 			return error
 		}
 	}
+	*/
 }
diff --git a/src/database/queries/user-role.js b/src/database/queries/user-role.js
index ec62f37f3..a64644c96 100644
--- a/src/database/queries/user-role.js
+++ b/src/database/queries/user-role.js
@@ -64,9 +64,9 @@ exports.findAllRoles = async (filter, attributes, options) => {
 	}
 }
 
-exports.updateRole = async (filter, updatedata) => {
+exports.updateRole = async (filter, updateData) => {
 	try {
-		return await UserRole.update(updatedata, {
+		return await UserRole.update(updateData, {
 			where: filter,
 			returning: true,
 		})
diff --git a/src/locales/en.json b/src/locales/en.json
index 0c44ebe9e..21726f8aa 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -103,13 +103,14 @@
 	"ROLE_CHANGE_APPROVED": "Your request for becoming a mentor has been approved. Check email or login again to start your journey.",
 	"MATERIALIZED_VIEW_GENERATED_SUCCESSFULLY": "Materialized views generated successfully",
 	"MATERIALIZED_VIEW_REFRESH_INITIATED_SUCCESSFULLY": "Materialized views refresh initiated successfully",
-	"ROLE_CREATED_SUCCESSFULLY": "Roles added successfully",
-	"ROLE_UPDATED_SUCCESSFULLY": "Roles updated successfully",
-	"ROLE_DELETED_SUCCESSFULLY": "Module deleted successfully",
-	"ROLES_FETCHED_SUCCESSFULLY": "Roles fetched successfully",
-	"ROLE_NOT_UPDATED": "Roles not updated",
-	"ROLE_ALREADY_DELETED_OR_ROLE_NOT_PRESENT": "Roles already exists OR Roles not present",
-	"ROLE_NOT_DELETED": "Roles not deleted",
+	"ROLE_CREATED_SUCCESSFULLY": "Role created successfully.",
+	"ROLE_UPDATED_SUCCESSFULLY": "Role updated successfully.",
+	"ROLE_DELETED_SUCCESSFULLY": "Role deleted successfully.",
+	"ROLES_FETCHED_SUCCESSFULLY": "Roles fetched successfully.",
+	"ROLE_IS_NOT_UNIQUE": "A role with this title already exists. Please choose a different title.",
+	"ROLE_NOT_UPDATED": "Role could not be updated.",
+	"ROLE_ALREADY_DELETED_OR_ROLE_NOT_PRESENT": "The role has already been deleted or does not exist.",
+	"ROLE_NOT_DELETED": "Role could not be deleted.",
 	"ROLES_HAS_EMPTY_LIST": "Empty roles list",
 	"COLUMN_DOES_NOT_EXISTS": "Role column does not exists",
 	"PERMISSION_DENIED": "You do not have the required permissions to access this resource. Please contact your administrator for assistance.",
diff --git a/src/services/user-role.js b/src/services/user-role.js
index 764f5c826..12032da20 100644
--- a/src/services/user-role.js
+++ b/src/services/user-role.js
@@ -17,58 +17,70 @@ const utils = require('@generics/utils')
 
 module.exports = class userRoleHelper {
 	/**
-	 * Create roles.
+	 * Create a new role.
 	 * @method
 	 * @name create
-	 * @param {Object} req - Request data.
-	 * @param {Object} req.body - Request body contains role creation details.
-	 * @param {String} req.body.title - Title of the role.
-	 * @param {Integer} req.body.userType - User type of the role.
-	 * @param {String} req.body.status - Role status.
-	 * @param {String} req.body.translations - Translation for roles.
-	 * @param {String} req.body.visibility - Visibility of the role.
-	 * @param {Integer} req.body.organization_id - Organization ID for the role.
-	 * @returns {JSON} - Response contains role creation details.
+	 * @param {Object} bodyData - Role creation data.
+	 * @param {string} bodyData.title - Title of the role.
+	 * @param {number} bodyData.userType - User type of the role.
+	 * @param {string} bodyData.status - Status of the role.
+	 * @param {string} bodyData.translations - Translations for the role.
+	 * @param {string} bodyData.visibility - Visibility of the role.
+	 * @param {number} userOrganizationId - ID of the organization creating the role.
+	 * @param {string} tenantCode - Tenant code of the requestor.
+	 * @returns {Promise<Object>} - Created role response.
 	 */
-	static async create(bodyData, userOrganizationId) {
+
+	static async create(bodyData, userOrganizationId, tenantCode) {
 		try {
 			bodyData.organization_id = userOrganizationId
+			bodyData.tenant_code = tenantCode
 			const roles = await roleQueries.create(bodyData)
 			return responses.successResponse({
 				statusCode: httpStatusCode.created,
 				message: 'ROLE_CREATED_SUCCESSFULLY',
 				result: {
+					id: roles.id,
 					title: roles.title,
 					user_type: roles.user_type,
 					status: roles.status,
 					visibility: roles.visibility,
 					organization_id: roles.organization_id,
+					tenant_code: roles.tenant_code,
 					translations: roles.translations,
 				},
 			})
 		} catch (error) {
+			if (error.name === common.SEQUELIZE_UNIQUE_CONSTRAINT_ERROR) {
+				return responses.failureResponse({
+					statusCode: httpStatusCode.conflict,
+					responseCode: 'CLIENT_ERROR',
+					message: 'ROLE_IS_NOT_UNIQUE',
+				})
+			}
 			throw error
 		}
 	}
 
 	/**
-	 * Update roles.
+	 * Update a role by ID.
 	 * @method
 	 * @name update
-	 * @param {Object} req - Request data.
-	 * @param {Object} req.body - Request body contains role update details.
-	 * @param {String} req.body.title - Title of the role.
-	 * @param {Integer} req.body.userType - User type of the role.
-	 * @param {String} req.body.status - Role status.
-	 * @param {String} req.body.visibility - Visibility of the role.
-	 * @param {String} req.body.translations - Translation for roles.
-	 * @param {Integer} req.body.organization_id - Organization ID for the role.
-	 * @returns {JSON} - Response contains role update details.
+	 * @param {number} id - Role ID to be updated.
+	 * @param {Object} bodyData - Role update data.
+	 * @param {string} bodyData.title - Title of the role.
+	 * @param {number} bodyData.userType - User type of the role.
+	 * @param {string} bodyData.status - Status of the role.
+	 * @param {string} bodyData.visibility - Visibility of the role.
+	 * @param {string} bodyData.translations - Translations for the role.
+	 * @param {number} userOrganizationId - ID of the organization.
+	 * @param {string} tenantCode - Tenant code of the requestor.
+	 * @returns {Promise<Object>} - Updated role response.
 	 */
 
-	static async update(id, bodyData, userOrganizationId) {
+	static async update(id, bodyData, userOrganizationId, tenantCode) {
 		try {
-			const filter = { id: id, organization_id: userOrganizationId }
+			const filter = { id: id, organization_id: userOrganizationId, tenant_code: tenantCode }
 			const [updateCount, updateRole] = await roleQueries.updateRole(filter, bodyData)
 			if (updateCount == 0) {
 				return responses.failureResponse({
@@ -90,25 +102,35 @@ module.exports = class userRoleHelper {
 				},
 			})
 		} catch (error) {
+			if (error.name === common.SEQUELIZE_UNIQUE_CONSTRAINT_ERROR) {
+				return responses.failureResponse({
+					statusCode: httpStatusCode.conflict,
+					responseCode: 'CLIENT_ERROR',
+					message: 'ROLE_IS_NOT_UNIQUE',
+				})
+			}
 			throw error
 		}
 	}
 
 	/**
-	 * Delete role.
+	 * Delete a role by ID.
 	 * @method
 	 * @name delete
-	 * @param {Object} req - Request data.
-	 * @returns {JSON} - Role deletion response.
+	 * @param {number} id - Role ID to be deleted.
+	 * @param {number} userOrganizationId - ID of the organization.
+	 * @param {string} tenantCode - Tenant code of the requestor.
+	 * @returns {Promise<Object>} - Deletion result response.
 	 */
-	static async delete(id, userOrganizationId) {
+
+	static async delete(id, userOrganizationId, tenantCode) {
 		try {
-			const filter = { id: id, organization_id: userOrganizationId }
+			const filter = { id: id, organization_id: userOrganizationId, tenant_code: tenantCode }
 			const deleteRole = await roleQueries.deleteRole(filter)
 
 			if (deleteRole === 0) {
 				return responses.failureResponse({
-					message: 'ROLE_NOT_DELETED',
+					message: 'ROLE_ALREADY_DELETED_OR_ROLE_NOT_PRESENT',
 					statusCode: httpStatusCode.bad_request,
 					responseCode: 'CLIENT_ERROR',
 				})
@@ -125,15 +147,17 @@ module.exports = class userRoleHelper {
 	}
 
 	/**
-	 * Get all available roles.
+	 * List roles for an organization and default roles.
 	 * @method
 	 * @name list
-	 * @param {Array(String)} req.body.filters - Filters.
-	 * @param {String} req.pageNo - Page number.
-	 * @param {String} req.pageSize - Page size limit.
-	 * @param {String} req.searchText - Search text.
-	 * @param {Integer} req.decodedToken.organization_id - user organization_id.
-	 * @returns {JSON} - Role list.
+	 * @param {Object} filters - Filter parameters.
+	 * @param {number} page - Current page number.
+	 * @param {number} limit - Number of records per page.
+	 * @param {string} search - Search text for role title.
+	 * @param {number} userOrganizationId - Organization ID of the requester.
+	 * @param {string} tenantCode - Tenant code of the requester.
+	 * @param {string} [language] - Preferred language for labels.
+	 * @returns {Promise<Object>} - Paginated list of roles.
 	 */
 
 	static async list(filters, page, limit, search, userOrganizationId, tenantCode, language) {
@@ -165,6 +189,7 @@ module.exports = class userRoleHelper {
 				'status',
 				'organization_id',
 				'translations',
+				'tenant_code',
 			]
 			const roles = await roleQueries.findAllRoles(filter, attributes, options)
 
@@ -200,16 +225,18 @@ module.exports = class userRoleHelper {
 	}
 
 	/**
-	 * Get all available roles.
+	 * @deprecated
+	 * This method is deprecated. Use `list()` instead.
 	 * @method
-	 * @name defaultlist
-	 * @param {Array(String)} req.body.filters - Filters.
-	 * @param {String} req.pageNo - Page number.
-	 * @param {String} req.pageSize - Page size limit.
-	 * @param {String} req.searchText - Search text.
-	 * @returns {JSON} - Role list.
+	 * @name defaultList
+	 * @param {Object} filters - Filter parameters.
+	 * @param {number} page - Current page number.
+	 * @param {number} limit - Number of records per page.
+	 * @param {string} search - Search text for role title.
+	 * @returns {Promise<Object>} - Paginated list of default organization roles.
 	 */
-	static async defaultList(filters, page, limit, search) {
+
+	/* static async defaultList(filters, page, limit, search) {
 		try {
 			delete filters.search
 			const offset = common.getPaginationOffset(page, limit)
@@ -250,5 +277,5 @@ module.exports = class userRoleHelper {
 		} catch (error) {
 			throw error
 		}
-	}
+	} */
 }

From f8c9a774ba56715b8827642cea6305fe1f29de33 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Wed, 6 Aug 2025 19:36:25 +0530
Subject: [PATCH 42/55] update: search api to work with tenant code and new FKs

---
 src/database/queries/users.js | 96 +++++++++++++++++++++++++++++++++++
 src/services/account.js       | 87 +++++++++++++++++--------------
 src/validators/v1/account.js  |  6 +++
 3 files changed, 152 insertions(+), 37 deletions(-)

diff --git a/src/database/queries/users.js b/src/database/queries/users.js
index 718115865..d2ac84458 100644
--- a/src/database/queries/users.js
+++ b/src/database/queries/users.js
@@ -388,6 +388,102 @@ exports.listUsersFromView = async (
 		throw error
 	}
 }
+exports.searchUsersWithOrganization = async ({
+	roleIds,
+	organization_id,
+	page,
+	limit,
+	search,
+	userIds,
+	emailIds,
+	excluded_user_ids,
+	tenantCode,
+}) => {
+	try {
+		const offset = (page - 1) * limit
+
+		// Base filter for user
+		const userWhere = {}
+
+		// Filter by userIds / exclude
+		if (userIds && Array.isArray(userIds)) {
+			userWhere.id = { [Op.in]: userIds }
+			if (excluded_user_ids && excluded_user_ids.length > 0) {
+				userWhere.id = {
+					[Op.and]: [{ [Op.in]: userIds }, { [Op.notIn]: excluded_user_ids }],
+				}
+			}
+		} else if (excluded_user_ids && excluded_user_ids.length > 0) {
+			userWhere.id = { [Op.notIn]: excluded_user_ids }
+		}
+
+		// Filter by search text or email
+		if (emailIds && emailIds.length > 0) {
+			userWhere.email = { [Op.in]: emailIds }
+		} else if (search) {
+			userWhere.name = { [Op.iLike]: `%${search}%` }
+		}
+
+		const users = await database.User.findAndCountAll({
+			where: userWhere,
+			limit: limit,
+			offset: offset,
+			order: [['name', 'ASC']],
+			include: [
+				{
+					model: database.UserOrganization,
+					as: 'user_organizations',
+					required: true,
+					where: {
+						tenant_code: tenantCode,
+						...(organization_id && { organization_id }),
+					},
+					include: [
+						{
+							model: database.Organization,
+							as: 'organization',
+							required: false,
+							where: {
+								status: 'ACTIVE',
+								tenant_code: tenantCode,
+							},
+							attributes: ['id', 'name', 'code'],
+						},
+						{
+							model: database.UserOrganizationRole,
+							as: 'roles',
+							required: roleIds && roleIds.length > 0,
+							where: {
+								tenant_code: tenantCode,
+								role_id: { [Op.in]: roleIds },
+							},
+							attributes: ['role_id'],
+							include: [
+								{
+									model: database.UserRole,
+									as: 'role',
+									required: false,
+									where: { tenant_code: tenantCode },
+									attributes: ['id', 'title', 'label'],
+								},
+							],
+						},
+					],
+				},
+			],
+			raw: false,
+			distinct: true, // Needed for correct count when using include
+		})
+
+		return {
+			count: users.count,
+			data: users.rows,
+		}
+	} catch (error) {
+		console.error('Error in searchUsersWithOrganization:', error)
+		throw error
+	}
+}
 
 exports.changeOrganization = async (id, currentOrgId, newOrgId, updateBody = {}) => {
 	const transaction = await Sequelize.transaction()
diff --git a/src/services/account.js b/src/services/account.js
index a7cdb626a..1dffd6368 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -1827,24 +1827,32 @@ module.exports = class AccountHelper {
 	}
 
 	/**
-	 * Account List
-	 * @method
-	 * @name list method post
-	 * @param {Object} req -request data.
-	 * @param {Array} userIds -contains userIds.
-	 * @returns {JSON} - all accounts data
-	 * User list.
-	 * @method
-	 * @name list method get
-	 * @param {Boolean} userType - mentor/mentee.
-	 * @param {Number} page - page No.
-	 * @param {Number} limit - page limit.
-	 * @param {String} search - search field.
-	 * @returns {JSON} - List of users
+	 * Search and list users based on filters such as role, email, search text, etc.
+	 *
+	 * Handles both POST-based user ID filtering and GET-based search queries.
+	 *
+	 * @async
+	 * @method search
+	 * @param {Object} params - Parameters for the search.
+	 * @param {Object} params.query - Query parameters from the request.
+	 * @param {string} params.query.tenant_code - Tenant identifier for scoping the search.
+	 * @param {string} params.query.type - Role type(s) to filter by (comma-separated, e.g., "mentor,mentee" or "all").
+	 * @param {string} [params.query.organization_id] - Organization ID for filtering users.
+	 * @param {string} [params.searchText] - Text input for searching users (can include emails or names).
+	 * @param {number} [params.pageNo] - Page number for pagination.
+	 * @param {number} [params.pageSize] - Number of users per page.
+	 * @param {Object} [params.body] - POST body parameters.
+	 * @param {Array<string>} [params.body.user_ids] - Specific user IDs to include in search.
+	 * @param {Array<string>} [params.body.excluded_user_ids] - User IDs to exclude from search.
+	 *
+	 * @returns {Promise<Object>} JSON response with user list and count.
 	 */
+
 	static async search(params) {
 		try {
-			let roleQuery = {}
+			let roleQuery = {
+				tenant_code: params.query.tenant_code,
+			}
 			if (params.query.type.toLowerCase() === common.TYPE_ALL) {
 				roleQuery.status = common.ACTIVE_STATUS
 			} else {
@@ -1869,29 +1877,18 @@ module.exports = class AccountHelper {
 				}
 			})
 
-			let users = await userQueries.listUsersFromView(
-				roleIds ? roleIds : [],
-				params.query.organization_id ? params.query.organization_id : '',
-				params.pageNo,
-				params.pageSize,
-				emailIds.length == 0 ? params.searchText : false,
-				params.body.user_ids ? params.body.user_ids : false,
-				emailIds.length > 0 ? emailIds : false,
-				params.body.excluded_user_ids ? params.body.excluded_user_ids : false
-			)
-
-			/* Required to resolve all promises first before preparing response object else sometime 
-					it will push unresolved promise object if you put this logic in below for loop */
+			let users = await userQueries.searchUsersWithOrganization({
+				roleIds,
+				organization_id: params.query.organization_id,
+				page: params.pageNo,
+				limit: params.pageSize,
+				search: emailIds.length == 0 ? params.searchText : false,
+				userIds: params.body.user_ids || false,
+				emailIds: emailIds.length > 0 ? emailIds : false,
+				excluded_user_ids: params.body.excluded_user_ids || false,
+				tenantCode: params.query.tenant_code,
+			})
 
-			await Promise.all(
-				users.data.map(async (user) => {
-					/* Assigned image url from the stored location */
-					if (user.image) {
-						user.image = await utilsHelper.getDownloadableUrl(user.image)
-					}
-					return user
-				})
-			)
 			if (users.count == 0) {
 				return responses.successResponse({
 					statusCode: httpStatusCode.ok,
@@ -1903,6 +1900,22 @@ module.exports = class AccountHelper {
 				})
 			}
 
+			/* Required to resolve all promises first before preparing response object else sometime 
+			it will push unresolved promise object if you put this logic in below for loop */
+			// Decrypt email and add image URL
+			await Promise.all(
+				users.data.map(async (user) => {
+					/* Assigned image url from the stored location */
+					if (user.image) {
+						user.image = await utilsHelper.getDownloadableUrl(user.image)
+					}
+					if (user.email) {
+						user.email = emailEncryption.decrypt(user.email)
+					}
+					return user
+				})
+			)
+
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,
 				message: 'USER_LIST',
diff --git a/src/validators/v1/account.js b/src/validators/v1/account.js
index 205dfe1d0..ab1bef776 100644
--- a/src/validators/v1/account.js
+++ b/src/validators/v1/account.js
@@ -240,6 +240,11 @@ module.exports = {
 	},
 
 	search: (req) => {
+		req.checkQuery('tenant_code')
+			.notEmpty()
+			.withMessage('tenant_code can not be null')
+			.isString()
+			.withMessage('tenant_code type value')
 		req.checkQuery('type')
 			.notEmpty()
 			.withMessage('type can not be null')
@@ -247,6 +252,7 @@ module.exports = {
 			.notIn([common.ADMIN_ROLE, common.MENTEE_ROLE, common.MENTEE_ROLE, common.ORG_ADMIN_ROLE, common.TYPE_ALL])
 			.withMessage('Invalid type value')
 		req.checkQuery('organization_id').isNumeric().withMessage('organization_id must be an Id')
+
 		req.checkBody('user_ids')
 			.isArray()
 			.withMessage('user_ids must be an array')

From 4af2bff5bb76b51a057f63372ca2cff8d8daa2d7 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Thu, 7 Aug 2025 13:24:12 +0530
Subject: [PATCH 43/55] add: role id as optional param

---
 src/database/queries/users.js | 2 +-
 src/services/account.js       | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/database/queries/users.js b/src/database/queries/users.js
index d2ac84458..c98c4d69f 100644
--- a/src/database/queries/users.js
+++ b/src/database/queries/users.js
@@ -455,7 +455,7 @@ exports.searchUsersWithOrganization = async ({
 							required: roleIds && roleIds.length > 0,
 							where: {
 								tenant_code: tenantCode,
-								role_id: { [Op.in]: roleIds },
+								...(roleIds && roleIds.length > 0 && { role_id: { [Op.in]: roleIds } }),
 							},
 							attributes: ['role_id'],
 							include: [
diff --git a/src/services/account.js b/src/services/account.js
index 1dffd6368..5b55899f1 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -1883,9 +1883,9 @@ module.exports = class AccountHelper {
 				page: params.pageNo,
 				limit: params.pageSize,
 				search: emailIds.length == 0 ? params.searchText : false,
-				userIds: params.body.user_ids || false,
+				userIds: params.body?.user_ids || false,
 				emailIds: emailIds.length > 0 ? emailIds : false,
-				excluded_user_ids: params.body.excluded_user_ids || false,
+				excluded_user_ids: params.body?.excluded_user_ids || false,
 				tenantCode: params.query.tenant_code,
 			})
 

From d9a6e20b3307db5a976c48f4a3b2f831157ffb92 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Fri, 8 Aug 2025 16:35:58 +0530
Subject: [PATCH 44/55] feat(org-admin): update deactivateUser endpoint to
 support tenant code

---
 src/controllers/v1/org-admin.js |   8 ---
 src/database/queries/users.js   |  66 ++++++++++++++++++
 src/helpers/userHelper.js       |  60 +++++++++++++++++
 src/services/org-admin.js       | 116 ++++++++++++++++++++++----------
 src/validators/v1/org-admin.js  |  28 ++++++--
 5 files changed, 232 insertions(+), 46 deletions(-)

diff --git a/src/controllers/v1/org-admin.js b/src/controllers/v1/org-admin.js
index 287852060..d39cf2167 100644
--- a/src/controllers/v1/org-admin.js
+++ b/src/controllers/v1/org-admin.js
@@ -161,14 +161,6 @@ module.exports = class OrgAdmin {
 				})
 			}
 
-			if (!req.body.id && !req.body.email) {
-				throw responses.failureResponse({
-					message: 'EMAIL_OR_ID_REQUIRED',
-					statusCode: httpStatusCode.bad_request,
-					responseCode: 'CLIENT_ERROR',
-				})
-			}
-
 			const result = await orgAdminService.deactivateUser(req.body, req.decodedToken)
 
 			return result
diff --git a/src/database/queries/users.js b/src/database/queries/users.js
index c98c4d69f..51246a988 100644
--- a/src/database/queries/users.js
+++ b/src/database/queries/users.js
@@ -519,3 +519,69 @@ exports.changeOrganization = async (id, currentOrgId, newOrgId, updateBody = {})
 		throw error
 	}
 }
+
+/**
+ * Deactivates users within a specific organization and tenant based on the provided filter.
+ *
+ * This function:
+ * - First fetches all users matching the `filter` and ensures they belong to the given `organization_code` and `tenant_code`.
+ * - Updates the matched users' records with the given `updateData`.
+ * - Optionally returns the list of matched user records if `returnUpdatedUsers` is set to true.
+ *
+ * Note:
+ * - Users not associated with the given organization/tenant are excluded.
+ * - This function currently assumes each user belongs to only one organization in this context.
+ *
+ * @async
+ * @param {Object} filter - Sequelize-compatible filter criteria for selecting users.
+ * @param {string} organization_code - Code of the organization to scope user lookup.
+ * @param {string} tenant_code - Tenant code for further scoping the user lookup.
+ * @param {Object} updateData - Fields to update for the matched users (e.g., status, updated_by).
+ * @param {boolean} [returnUpdatedUsers=false] - Whether to return the list of matched user records.
+ *
+ * @returns {Promise<[number, Object[]]>} A tuple:
+ *  - First item: Number of rows affected by the update.
+ *  - Second item: Array of user records if `returnUpdatedUsers` is true, otherwise an empty array.
+ *
+ * @throws {Error} Throws if there is any issue during the query or update.
+ */
+
+exports.deactivateUserInOrg = async (
+	filter,
+	organization_code,
+	tenant_code,
+	updateData,
+	returnUpdatedUsers = false
+) => {
+	try {
+		const users = await database.User.findAll({
+			where: filter,
+			include: [
+				{
+					model: database.UserOrganization,
+					as: 'user_organizations',
+					required: true,
+					where: {
+						organization_code,
+						tenant_code,
+					},
+					attributes: [],
+				},
+			],
+			attributes: ['id'],
+		})
+
+		const userIds = users.map((u) => u.id)
+
+		if (userIds.length === 0) return [0, []]
+
+		const [rowsAffected] = await database.User.update(updateData, {
+			where: { id: { [Op.in]: userIds } },
+		})
+
+		return [rowsAffected, returnUpdatedUsers ? users : []]
+	} catch (error) {
+		console.error('Error in deactivateUserInOrg:', error)
+		throw error
+	}
+}
diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js
index 58b93cd50..d3c160988 100644
--- a/src/helpers/userHelper.js
+++ b/src/helpers/userHelper.js
@@ -7,6 +7,7 @@ const userSessionsService = require('@services/user-sessions')
 const Sequelize = require('@database/models/index').sequelize
 const REDIS_USER_PREFIX = common.redisUserPrefix
 const DELETED_STATUS = common.DELETED_STATUS
+const userSessionsQueries = require('@database/queries/user-sessions')
 
 function generateUpdateParams(userId) {
 	return {
@@ -94,6 +95,65 @@ const userHelper = {
 			transactionOptions
 		)
 	},
+
+	/**
+	 * Removes all active sessions for the specified user(s) within a tenant.
+	 *
+	 * This function performs the following:
+	 * - Accepts one or more user IDs and ensures they belong to the given tenant.
+	 * - Retrieves all active sessions (where `ended_at` is null).
+	 * - Deletes associated session keys from Redis.
+	 * - Marks the sessions as ended in the database by setting `ended_at` to the current timestamp.
+	 *
+	 * @async
+	 * @param {number|number[]} userIds - A single user ID or an array of user IDs whose sessions should be removed.
+	 * @param {string} tenantCode - The tenant code used to scope session lookup.
+	 *
+	 * @returns {Promise<{ success: boolean, removedCount: number }>} Object indicating success status and number of sessions removed.
+	 *
+	 * @throws {Error} If any step in the removal process fails (Redis or DB update).
+	 */
+
+	async removeAllUserSessions(userIds, tenantCode) {
+		try {
+			const userIdArray = Array.isArray(userIds) ? userIds : [userIds]
+
+			if (userIdArray.length === 0) {
+				return { success: true, removedCount: 0 }
+			}
+
+			// Find all active sessions for the user(s)
+			const sessions = await userSessionsQueries.findAll(
+				{
+					user_id: userIdArray,
+					tenant_code: tenantCode,
+					ended_at: null,
+				},
+				{ attributes: ['id'] }
+			)
+
+			const sessionIds = sessions.map((s) => s.id)
+
+			if (sessionIds.length === 0) {
+				return { success: true, removedCount: 0 }
+			}
+
+			// Delete from Redis
+			await Promise.all(sessionIds.map((id) => utils.redisDel(id.toString())))
+
+			// Mark sessions as ended in DB
+			const currentTime = Math.floor(Date.now() / 1000)
+			const updateResult = await userSessionsQueries.update({ id: sessionIds }, { ended_at: currentTime })
+
+			if (updateResult instanceof Error) {
+				throw updateResult
+			}
+
+			return { success: true, removedCount: sessionIds.length }
+		} catch (error) {
+			throw new Error(`Failed to remove sessions: ${error.message}`)
+		}
+	},
 }
 
 module.exports = userHelper
diff --git a/src/services/org-admin.js b/src/services/org-admin.js
index 2ead572d3..0dc060fd5 100644
--- a/src/services/org-admin.js
+++ b/src/services/org-admin.js
@@ -22,11 +22,12 @@ const userOrganizationRoleQueries = require('@database/queries/userOrganizationR
 const { eventBroadcaster } = require('@helpers/eventBroadcaster')
 const { Queue } = require('bullmq')
 const { Op } = require('sequelize')
-const UserCredentialQueries = require('@database/queries/userCredential')
+
 const tenantDomainQueries = require('@database/queries/tenantDomain')
 const emailEncryption = require('@utils/emailEncryption')
 const responses = require('@helpers/responses')
 const notificationUtils = require('@utils/notification')
+const userHelper = require('@helpers/userHelper')
 
 module.exports = class OrgAdminHelper {
 	/**
@@ -400,46 +401,85 @@ module.exports = class OrgAdminHelper {
 	}
 
 	/**
-	 * Deactivate User
-	 * @method
-	 * @name deactivateUser
-	 * @param {Number} id - user id
-	 * @param {Object} loggedInUserId - logged in user id
-	 * @returns {JSON} - Deactivated user data
+	 * Deactivates users by their IDs or email addresses within a specific organization and tenant.
+	 *
+	 * This function performs the following:
+	 * - Deactivates users by matching user IDs (if provided) under the same tenant and organization.
+	 * - Deactivates users by matching emails (if provided) under the same tenant and organization.
+	 * - Broadcasts an event to handle cleanup of upcoming sessions for deactivated users.
+	 * - Removes all active sessions for the affected users.
+	 *
+	 * Note:
+	 * - This function currently does **not** support users associated with multiple organizations.
+	 *   It only considers users under the organization specified in `tokenInformation.organization_code`.
+	 *
+	 * @async
+	 * @param {Object} bodyData - Request payload containing user identifiers.
+	 * @param {number[]} [bodyData.ids] - Optional array of user IDs to deactivate.
+	 * @param {string[]} [bodyData.emails] - Optional array of user emails to deactivate.
+	 *
+	 * @param {Object} tokenInformation - Authenticated user's context information.
+	 * @param {string} tokenInformation.organization_code - The organization code for scoping the operation.
+	 * @param {string} tokenInformation.tenant_code - The tenant code to match users within the same tenant.
+	 * @param {number} tokenInformation.id - The ID of the user initiating the deactivation (used as `updated_by`).
+	 *
+	 * @returns {Promise<Object>} Response object with success or failure details.
+	 * - If successful, includes the user IDs that were deactivated.
+	 * - If no users were updated, returns a failure response with appropriate status.
+	 *
+	 * @throws {Error} Throws error on unexpected failure during the deactivation process.
 	 */
+
 	static async deactivateUser(bodyData, tokenInformation) {
 		try {
-			let filterQuery = {
-				organization_id: tokenInformation.organization_id,
-			}
+			const { ids = [], emails = [] } = bodyData
 
-			for (let item in bodyData) {
-				filterQuery[item] = {
-					[Op.in]: bodyData[item],
-				}
+			let totalRowsAffected = 0
+			const updatedByIds = []
+			const updatedByEmails = []
+
+			// Deactivate by IDs
+			if (ids.length) {
+				const [rowsAffected, updatedUsers] = await userQueries.deactivateUserInOrg(
+					{
+						id: { [Op.in]: ids },
+						tenant_code: tokenInformation.tenant_code,
+					},
+					tokenInformation.organization_code,
+					tokenInformation.tenant_code,
+					{
+						status: common.INACTIVE_STATUS,
+						updated_by: tokenInformation.id,
+					},
+					true // pass flag to return matched users (optional)
+				)
+				console.log(rowsAffected, updatedUsers)
+				totalRowsAffected += rowsAffected
+				updatedByIds.push(...updatedUsers.map((user) => user.id))
 			}
-			let userIds = []
 
-			if (bodyData.email) {
-				const encryptedEmailIds = bodyData.email.map((email) => emailEncryption.encrypt(email.toLowerCase()))
-				const userCredentials = await UserCredentialQueries.findAll(
-					{ email: { [Op.in]: encryptedEmailIds } },
+			// Deactivate by Emails
+			if (emails.length) {
+				const [rowsAffected, updatedUsers] = await userQueries.deactivateUserInOrg(
 					{
-						attributes: ['user_id'],
-					}
+						email: { [Op.in]: emails.map((email) => emailEncryption.encrypt(email.toLowerCase())) },
+						tenant_code: tokenInformation.tenant_code,
+					},
+					tokenInformation.organization_code,
+					tokenInformation.tenant_code,
+					{
+						status: common.INACTIVE_STATUS,
+						updated_by: tokenInformation.id,
+					},
+					true // return updated users
 				)
-				userIds = _.map(userCredentials, 'user_id')
-				delete filterQuery.email
-				filterQuery.id = userIds
-			} else {
-				userIds = bodyData.id
+
+				totalRowsAffected += rowsAffected
+				updatedByEmails.push(...updatedUsers.map((user) => user.id))
 			}
-			let [rowsAffected] = await userQueries.updateUser(filterQuery, {
-				status: common.INACTIVE_STATUS,
-				updated_by: tokenInformation.id,
-			})
 
-			if (rowsAffected == 0) {
+			// If nothing was deactivated
+			if (totalRowsAffected === 0) {
 				return responses.failureResponse({
 					message: 'STATUS_UPDATE_FAILED',
 					statusCode: httpStatusCode.bad_request,
@@ -447,19 +487,27 @@ module.exports = class OrgAdminHelper {
 				})
 			}
 
-			//check and deactivate upcoming sessions
+			// Broadcast event
+			const allUserIds = [...new Set([...updatedByIds, ...updatedByEmails])]
+
+			userHelper.removeAllUserSessions(allUserIds, tokenInformation.tenant_code)
+
 			eventBroadcaster('deactivateUpcomingSession', {
 				requestBody: {
-					user_ids: userIds,
+					user_ids: allUserIds,
 				},
 			})
 
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,
 				message: 'USER_DEACTIVATED',
+				result: {
+					updated_by_ids: updatedByIds,
+					updated_by_emails: updatedByEmails,
+				},
 			})
 		} catch (error) {
-			console.log(error)
+			console.error('Error in deactivateUser:', error)
 			throw error
 		}
 	}
diff --git a/src/validators/v1/org-admin.js b/src/validators/v1/org-admin.js
index 52d44e437..a6deeca56 100644
--- a/src/validators/v1/org-admin.js
+++ b/src/validators/v1/org-admin.js
@@ -28,10 +28,30 @@ module.exports = {
 		req.checkBody('status').notEmpty().withMessage('status field is empty')
 	},
 	deactivateUser: (req) => {
-		const field = req.body.email ? 'email' : req.body.id ? 'id' : null
-		if (field) {
-			req.checkBody(field).isArray().notEmpty().withMessage(` ${field} must be an array and should not be empty.`)
-		}
+		req.checkBody('emails').optional().isArray().withMessage('The "emails" field must be an array, if provided.')
+
+		req.checkBody('emails.*')
+			.optional()
+			.isEmail()
+			.withMessage('Each item in the "emails" array must be a valid email address.')
+
+		req.checkBody('ids').optional().isArray().withMessage('The "ids" field must be an array, if provided.')
+
+		req.checkBody('ids.*')
+			.optional()
+			.isNumeric()
+			.withMessage('Each item in the "ids" array must be a numeric value.')
+
+		req.checkBody(['emails', 'ids']).custom(() => {
+			const ids = req.body.ids
+			const emails = req.body.emails
+
+			if (!emails && !ids) {
+				throw new Error('At least one of "emails" or "ids" must be provided.')
+			}
+
+			return true
+		})
 	},
 	inheritEntityType: (req) => {
 		// Validate incoming request body

From fc8f3d06817e7af9de646b5c6188565585535adf Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Fri, 8 Aug 2025 20:55:03 +0530
Subject: [PATCH 45/55] improved validation

---
 src/validators/v1/org-admin.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/validators/v1/org-admin.js b/src/validators/v1/org-admin.js
index a6deeca56..fe76893d4 100644
--- a/src/validators/v1/org-admin.js
+++ b/src/validators/v1/org-admin.js
@@ -46,8 +46,8 @@ module.exports = {
 			const ids = req.body.ids
 			const emails = req.body.emails
 
-			if (!emails && !ids) {
-				throw new Error('At least one of "emails" or "ids" must be provided.')
+			if ((!emails || emails.length === 0) && (!ids || ids.length === 0)) {
+				throw new Error('Provide at least one non-empty "emails" or "ids" array.')
 			}
 
 			return true

From a9538e3c18be29e6daf1b4ca20508d829f74ee4e Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Fri, 8 Aug 2025 20:55:54 +0530
Subject: [PATCH 46/55] add: await to removeAllUserSessions

---
 src/services/org-admin.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/services/org-admin.js b/src/services/org-admin.js
index 0dc060fd5..3ae9964dd 100644
--- a/src/services/org-admin.js
+++ b/src/services/org-admin.js
@@ -490,7 +490,7 @@ module.exports = class OrgAdminHelper {
 			// Broadcast event
 			const allUserIds = [...new Set([...updatedByIds, ...updatedByEmails])]
 
-			userHelper.removeAllUserSessions(allUserIds, tokenInformation.tenant_code)
+			await userHelper.removeAllUserSessions(allUserIds, tokenInformation.tenant_code)
 
 			eventBroadcaster('deactivateUpcomingSession', {
 				requestBody: {

From 908ef73b42a568dc9c053563df512004702f5534 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Fri, 8 Aug 2025 20:59:01 +0530
Subject: [PATCH 47/55] add: tenantCode to user query

---
 src/database/queries/users.js | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/src/database/queries/users.js b/src/database/queries/users.js
index 51246a988..fef160021 100644
--- a/src/database/queries/users.js
+++ b/src/database/queries/users.js
@@ -546,13 +546,7 @@ exports.changeOrganization = async (id, currentOrgId, newOrgId, updateBody = {})
  * @throws {Error} Throws if there is any issue during the query or update.
  */
 
-exports.deactivateUserInOrg = async (
-	filter,
-	organization_code,
-	tenant_code,
-	updateData,
-	returnUpdatedUsers = false
-) => {
+exports.deactivateUserInOrg = async (filter, organizationCode, tenantCode, updateData, returnUpdatedUsers = false) => {
 	try {
 		const users = await database.User.findAll({
 			where: filter,
@@ -562,8 +556,8 @@ exports.deactivateUserInOrg = async (
 					as: 'user_organizations',
 					required: true,
 					where: {
-						organization_code,
-						tenant_code,
+						organization_code: organizationCode,
+						tenant_code: tenantCode,
 					},
 					attributes: [],
 				},
@@ -576,7 +570,7 @@ exports.deactivateUserInOrg = async (
 		if (userIds.length === 0) return [0, []]
 
 		const [rowsAffected] = await database.User.update(updateData, {
-			where: { id: { [Op.in]: userIds } },
+			where: { id: { [Op.in]: userIds, tenant_code: tenantCode } },
 		})
 
 		return [rowsAffected, returnUpdatedUsers ? users : []]

From 8e2c8f05dc8ac801ae5791579908f718f7e5c98d Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Fri, 8 Aug 2025 21:02:22 +0530
Subject: [PATCH 48/55] fix: deactivateUserInOrg

---
 src/database/queries/users.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/database/queries/users.js b/src/database/queries/users.js
index fef160021..75a004fd7 100644
--- a/src/database/queries/users.js
+++ b/src/database/queries/users.js
@@ -570,7 +570,7 @@ exports.deactivateUserInOrg = async (filter, organizationCode, tenantCode, updat
 		if (userIds.length === 0) return [0, []]
 
 		const [rowsAffected] = await database.User.update(updateData, {
-			where: { id: { [Op.in]: userIds, tenant_code: tenantCode } },
+			where: { id: { [Op.in]: userIds }, tenant_code: tenantCode },
 		})
 
 		return [rowsAffected, returnUpdatedUsers ? users : []]

From bce9b4f733856ff2a2e62e423d85b422ab2e32ec Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Tue, 12 Aug 2025 13:21:45 +0530
Subject: [PATCH 49/55] fix: password policy message

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

diff --git a/src/envVariables.js b/src/envVariables.js
index 6d38bf056..dbb6ee3e2 100644
--- a/src/envVariables.js
+++ b/src/envVariables.js
@@ -209,7 +209,7 @@ let enviromentVariables = {
 		message: 'Required password policy message',
 		optional: true,
 		default:
-			'Password must have at least one uppercase letter, one number, one special character, and be at least 10 characters long',
+			'Password must have at least two uppercase letters, two numbers, three special characters, and be at least 11 characters long.',
 	},
 	DOWNLOAD_URL_EXPIRATION_DURATION: {
 		message: 'Required downloadable url expiration time',

From a96b870a7fd404d0f263cf3b332bf27132276b0b Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Tue, 12 Aug 2025 14:29:49 +0530
Subject: [PATCH 50/55] feat: deactivateOrg to work with tenant code

---
 src/controllers/v1/admin.js | 32 +++++++++++++---
 src/services/admin.js       | 76 ++++++++++++++++++++++---------------
 2 files changed, 71 insertions(+), 37 deletions(-)

diff --git a/src/controllers/v1/admin.js b/src/controllers/v1/admin.js
index a7f9128cc..b18b4c3ff 100644
--- a/src/controllers/v1/admin.js
+++ b/src/controllers/v1/admin.js
@@ -120,12 +120,28 @@ module.exports = class Admin {
 	}
 
 	/**
-	 * Deactivate Org
-	 * @method
-	 * @name deactivateOrg
-	 * @param {String} req.params.id - org Id.
-	 * @returns {JSON} - deactivated org response
+	 * Deactivate an organization by its ID.
+	 *
+	 * Validates that the requesting user has admin access before deactivating
+	 * the specified organization and all associated users.
+	 *
+	 * @async
+	 * @method deactivateOrg
+	 * @param {Object} req - Express request object.
+	 * @param {string} req.params.id - The unique identifier (ID or code) of the organization to deactivate.
+	 * @param {Object} req.decodedToken - Decoded JWT token of the authenticated user.
+	 * @param {string[]} req.decodedToken.roles - Roles assigned to the logged-in user.
+	 * @param {number} req.decodedToken.id - ID of the logged-in user.
+	 * @param {Object} req.headers - HTTP request headers.
+	 * @param {string} req.headers.tenant-id - Tenant code associated with the request.
+	 * @returns {Promise<Object>} Response object from `adminService.deactivateOrg`,
+	 * containing status, message, and deactivated user count.
+	 *
+	 * @throws {Object} Failure response if:
+	 *  - The user is not an admin.
+	 *  - The organization cannot be deactivated.
 	 */
+
 	async deactivateOrg(req) {
 		try {
 			if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ADMIN_ROLE)) {
@@ -136,7 +152,11 @@ module.exports = class Admin {
 				})
 			}
 
-			const result = await adminService.deactivateOrg(req.params.id, req.decodedToken.id)
+			const result = await adminService.deactivateOrg(
+				req.params.id,
+				req.headers?.['tenant-id'],
+				req.decodedToken.id
+			)
 			return result
 		} catch (error) {
 			return error
diff --git a/src/services/admin.js b/src/services/admin.js
index b088777ee..4b515d6a2 100644
--- a/src/services/admin.js
+++ b/src/services/admin.js
@@ -568,19 +568,32 @@ module.exports = class AdminHelper {
 	}
 
 	/**
-	 * Deactivate Organization
-	 * @method
-	 * @name deactivateOrg
-	 * @param {Number} id - org id
-	 * @param {Object} loggedInUserId - logged in user id
-	 * @returns {JSON} - Deactivated user count
+	 * Deactivate an organization and all its associated users.
+	 *
+	 * This method:
+	 * 1. Updates the organization's status to inactive.
+	 * 2. Deactivates all users belonging to the organization.
+	 * 3. Ends all active sessions for the deactivated users.
+	 * 4. Broadcasts an event to end any upcoming sessions.
+	 *
+	 * @async
+	 * @function deactivateOrg
+	 * @param {string} organizationCode - The unique code identifying the organization.
+	 * @param {string} tenantCode - The tenant code to which the organization belongs.
+	 * @param {number} loggedInUserId - The ID of the user performing the deactivation.
+	 * @returns {Promise<Object>} Success or failure response object containing:
+	 *  - {number} result.deactivated_users - The number of users deactivated.
+	 *
+	 * @throws {Error} Will throw an error if the organization status update fails or if a database error occurs.
 	 */
-	static async deactivateOrg(id, loggedInUserId) {
+
+	static async deactivateOrg(organizationCode, tenantCode, loggedInUserId) {
 		try {
-			//deactivate org
-			let rowsAffected = await organizationQueries.update(
+			// 1. Deactivate org
+			const orgRowsAffected = await organizationQueries.update(
 				{
-					id,
+					code: organizationCode,
+					tenant_code: tenantCode,
 				},
 				{
 					status: common.INACTIVE_STATUS,
@@ -588,7 +601,7 @@ module.exports = class AdminHelper {
 				}
 			)
 
-			if (rowsAffected == 0) {
+			if (orgRowsAffected === 0) {
 				return responses.failureResponse({
 					message: 'STATUS_UPDATE_FAILED',
 					statusCode: httpStatusCode.bad_request,
@@ -596,42 +609,43 @@ module.exports = class AdminHelper {
 				})
 			}
 
-			//deactivate all users in org
-			const [modifiedCount] = await userQueries.updateUser(
+			// 2. Deactivate all users in the org using the same helper as deactivateUser
+			const [userRowsAffected, updatedUsers] = await userQueries.deactivateUserInOrg(
 				{
-					organization_id: id,
+					tenant_code: tenantCode,
 				},
+				organizationCode,
+				tenantCode,
 				{
 					status: common.INACTIVE_STATUS,
 					updated_by: loggedInUserId,
-				}
-			)
-
-			const users = await userQueries.findAll(
-				{
-					organization_id: id,
 				},
-				{
-					attributes: ['id'],
-				}
+				true // so we can get the user IDs
 			)
 
-			const userIds = _.map(users, 'id')
-			eventBroadcaster('deactivateUpcomingSession', {
-				requestBody: {
-					user_ids: userIds,
-				},
-			})
+			// 3. Broadcast & remove sessions if users were found
+			if (userRowsAffected > 0) {
+				const userIds = updatedUsers.map((u) => u.id)
 
+				// End all active sessions for those users
+				await userHelper.removeAllUserSessions(userIds, tenantCode)
+
+				// Broadcast to end upcoming sessions
+				eventBroadcaster('deactivateUpcomingSession', {
+					requestBody: { user_ids: userIds },
+				})
+			}
+
+			// 4. Return success
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,
 				message: 'ORG_DEACTIVATED',
 				result: {
-					deactivated_users: modifiedCount,
+					deactivated_users: userRowsAffected,
 				},
 			})
 		} catch (error) {
-			console.log(error)
+			console.error('Error in deactivateOrg:', error)
 			throw error
 		}
 	}

From ee0c85514418306b26ddf1c92baef7f7a3d9db98 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Tue, 12 Aug 2025 19:50:45 +0530
Subject: [PATCH 51/55] fix: admin login to work with tenant flow

---
 src/services/admin.js | 199 ++++++++++++++++++++++++++----------------
 1 file changed, 122 insertions(+), 77 deletions(-)

diff --git a/src/services/admin.js b/src/services/admin.js
index 4b515d6a2..b1f633357 100644
--- a/src/services/admin.js
+++ b/src/services/admin.js
@@ -9,6 +9,7 @@
 // Third-party libraries
 const _ = require('lodash')
 const { Op } = require('sequelize')
+const bcryptJs = require('bcryptjs')
 
 // Constants and generics
 const common = require('@constants/common')
@@ -232,103 +233,144 @@ module.exports = class AdminHelper {
 	}
 
 	/**
-	 * login admin user
-	 * @method
-	 * @name login
-	 * @param {Object} bodyData - user login data.
-	 * @param {string} bodyData.email - email.
-	 * @param {string} bodyData.password - email.
-	 * @param {string} deviceInformation - device information.
-	 * @returns {JSON} - returns login response
+	 * Handles the login process for admin users.
+	 *
+	 * This method validates user credentials and enforces additional checks
+	 * (such as admin role verification and active session limits) before
+	 * generating access/refresh tokens and creating a user session.
+	 *
+	 * Steps performed:
+	 *  1. Normalize and validate the identifier (email, phone, or username).
+	 *  2. Construct the user lookup query using the DEFAULT_TENANT_CODE.
+	 *  3. Retrieve the user with their associated organizations and roles.
+	 *  4. Verify the user's password using bcrypt.
+	 *  5. Ensure the user has the required admin role (`common.ADMIN_ROLE`).
+	 *  6. Enforce allowed active session limits (if configured).
+	 *  7. Create a new user session record.
+	 *  8. Enrich token payload with user and organization details.
+	 *  9. Generate access and refresh tokens.
+	 * 10. Remove sensitive data and process user image URLs.
+	 * 11. Update session details in Redis for active tracking.
+	 * 12. Return tokens and user details in the success response.
+	 *
+	 * @async
+	 * @param {Object} bodyData - Login request payload.
+	 * @param {string} [bodyData.identifier] - The user's email, phone, or username.
+	 * @param {string} [bodyData.email] - The user's email (alternative to `identifier`).
+	 * @param {string} [bodyData.phone_code] - Phone country code (required if using phone login).
+	 * @param {string} bodyData.password - The user's password.
+	 * @param {Object} deviceInformation - Metadata about the device used for login.
+	 *
+	 * @returns {Promise<Object>} A success response containing:
+	 *  - `access_token` {string} - JWT for API access.
+	 *  - `refresh_token` {string} - JWT for refreshing sessions.
+	 *  - `user` {Object} - Sanitized user object with organization details.
+	 *
+	 * @throws {Error} Rethrows unexpected errors for global error handling.
+	 *
+	 * @example
+	 * const loginResponse = await AuthService.login(
+	 *   { identifier: 'admin@example.com', password: 'StrongPass123!' },
+	 *   { ip: '192.168.1.1', device: 'Chrome on Windows' }
+	 * );
+	 * console.log(loginResponse.result.access_token);
 	 */
+
 	static async login(bodyData, deviceInformation) {
 		try {
-			const plaintextEmailId = bodyData.email.toLowerCase()
-			const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId)
-			const userCredentials = await UserCredentialQueries.findOne({ email: encryptedEmailId })
-			if (!userCredentials) {
-				return responses.failureResponse({
-					message: 'USER_DOESNOT_EXISTS',
-					statusCode: httpStatusCode.bad_request,
+			// helper for consistent failure responses
+			const failure = (message, status = httpStatusCode.bad_request) =>
+				responses.failureResponse({
+					message,
+					statusCode: status,
 					responseCode: 'CLIENT_ERROR',
 				})
+
+			// 1) Identifier handling: accept `identifier` or fallback to `email`
+			const rawIdentifier = (bodyData.identifier || bodyData.email || '').toString().trim()
+			const identifier = rawIdentifier.toLowerCase()
+			if (!identifier) return failure('IDENTIFIER_REQUIRED', httpStatusCode.bad_request)
+
+			// identifier type helpers
+			const isEmail = (str) => /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(str)
+			const isPhone = (str) => /^\+?[1-9]\d{1,14}$/.test(str)
+			const isUsername = (str) => /^[a-zA-Z0-9_]{3,30}$/.test(str)
+
+			// 2) Build query (skip domain checks; use DEFAULT_TENANT_CODE)
+			const query = {
+				[Op.or]: [],
+				password: { [Op.ne]: null },
+				status: common.ACTIVE_STATUS,
+				tenant_code: process.env.DEFAULT_TENANT_CODE,
 			}
 
-			let user = await userQueries.findOne({
-				id: userCredentials.user_id,
-				// organization_id: userCredentials.organization_id,
-			})
-			if (!user.id) {
-				return responses.failureResponse({
-					message: 'USER_DOESNOT_EXISTS',
-					statusCode: httpStatusCode.bad_request,
-					responseCode: 'CLIENT_ERROR',
+			if (isEmail(identifier)) {
+				query[Op.or].push({ email: emailEncryption.encrypt(identifier) })
+			} else if (isPhone(identifier)) {
+				// expects bodyData.phone_code when phone login is used
+				query[Op.or].push({
+					phone: emailEncryption.encrypt(identifier),
+					phone_code: bodyData.phone_code,
 				})
+			} else {
+				query[Op.or].push({ username: identifier })
 			}
-			const isPasswordCorrect = utils.comparePassword(bodyData.password, user.password)
+
+			// 3) Find user (reuse the helper that returns org associations like user/login)
+			const userInstance = await userQueries.findUserWithOrganization(query, {}, true)
+			let user = userInstance ? userInstance.toJSON() : null
+
+			if (!user) return failure('IDENTIFIER_OR_PASSWORD_INVALID', httpStatusCode.bad_request)
+
+			//Password verification (bcrypt async compare)
+			const isPasswordCorrect = await bcryptJs.compare(bodyData.password, user.password)
 			if (!isPasswordCorrect) {
-				return responses.failureResponse({
-					message: 'PASSWORD_INVALID',
-					statusCode: httpStatusCode.bad_request,
-					responseCode: 'CLIENT_ERROR',
-				})
+				return failure('IDENTIFIER_OR_PASSWORD_INVALID', httpStatusCode.bad_request)
 			}
 
-			let roles = await roleQueries.findAll(
-				{ id: user.roles },
-				{
-					attributes: {
-						exclude: ['created_at', 'updated_at', 'deleted_at'],
-					},
-				}
+			// Check for admin role
+			const hasAdminRole = user.user_organizations?.some((org) =>
+				org.roles?.some((r) => r.role?.title?.toLowerCase() === common.ADMIN_ROLE)
 			)
-			if (!roles) {
-				return responses.failureResponse({
-					message: 'ROLE_NOT_FOUND',
-					statusCode: httpStatusCode.not_acceptable,
-					responseCode: 'CLIENT_ERROR',
-				})
+
+			if (!hasAdminRole) {
+				return failure('IDENTIFIER_OR_PASSWORD_INVALID', httpStatusCode.bad_request)
+			}
+			// 4) Active session limit enforcement (if configured)
+			if (process.env.ALLOWED_ACTIVE_SESSIONS != null) {
+				const activeSessionCount = await userSessionsService.activeUserSessionCounts(user.id)
+				if (activeSessionCount >= Number(process.env.ALLOWED_ACTIVE_SESSIONS)) {
+					return failure('ACTIVE_SESSION_LIMIT_EXCEEDED', httpStatusCode.not_acceptable)
+				}
 			}
 
-			// create user session entry and add session_id to token data
+			// 6) Create user session
 			const userSessionDetails = await userSessionsService.createUserSession(
-				user.id, // userid
-				'', // refresh token
-				'', // Access token
-				deviceInformation
+				user.id,
+				'', // refresh token placeholder
+				'', // access token placeholder
+				deviceInformation,
+				user.tenant_code
 			)
 
-			/**
-			 * Based on user organisation id get user org parent Id value
-			 * If parent org id is present then set it to tenant of user
-			 * if not then set user organisation id to tenant
-			 */
-
-			// let tenantDetails = await organizationQueries.findOne(
-			// 	{ id: user.organization_id },
-			// 	{ attributes: ['parent_id'] }
-			// )
+			// 7) Token payload enrichment - follow same shape as user/login
+			// Ensure organizations exist; if not, create a default org object from env
 
-			// const tenant_id =
-			// 	tenantDetails && tenantDetails.parent_id !== null ? tenantDetails.parent_id : user.organization_id
+			user = UserTransformDTO.transform(user)
 
 			const tokenDetail = {
 				data: {
 					id: user.id,
 					name: user.name,
 					session_id: userSessionDetails.result.id,
-					organizations: [
-						{
-							id: process.env.DEFAULT_ORG_ID,
-							roles: roles,
-						},
-					],
-					tenant_code: process.env.DEFAULT_TENANT_CODE,
+					organization_ids: user.organizations.map((o) => String(o.id)),
+					organization_codes: user.organizations.map((o) => String(o.code)),
+					organizations: user.organizations,
+					tenant_code: user.tenant_code,
 				},
 			}
 
-			user.user_roles = roles
-
+			// 8) Generate tokens (same helper as user/login)
 			const accessToken = utils.generateToken(
 				tokenDetail,
 				process.env.ACCESS_TOKEN_SECRET,
@@ -340,20 +382,23 @@ module.exports = class AdminHelper {
 				common.refreshTokenExpiry
 			)
 
-			/**
-			 * This function call will do below things
-			 * 1: create redis entry for the session
-			 * 2: update user-session with token and refresh_token
-			 */
+			// 9) Remove sensitive fields and post-process user image
+			delete user.password
+			if (user && user.image) {
+				user.image = await utils.getDownloadableUrl(user.image)
+			}
+
+			// 10) Return original identifier and result
+			user.identifier = identifier
+			const result = { access_token: accessToken, refresh_token: refreshToken, user }
+
+			// 11) Update session and set Redis data (same flow as user/login)
 			await userSessionsService.updateUserSessionAndsetRedisData(
 				userSessionDetails.result.id,
 				accessToken,
 				refreshToken
 			)
 
-			delete user.password
-			const result = { access_token: accessToken, refresh_token: refreshToken, user }
-
 			return responses.successResponse({
 				statusCode: httpStatusCode.ok,
 				message: 'LOGGED_IN_SUCCESSFULLY',

From 5d6117e3c16d70d2e6afe68f7d6eada41cba8743 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Tue, 12 Aug 2025 20:14:59 +0530
Subject: [PATCH 52/55] fix: issue with createUserSession

---
 src/services/account.js       | 4 ----
 src/services/organization.js  | 2 ++
 src/services/user-sessions.js | 2 +-
 3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/services/account.js b/src/services/account.js
index 984ae1fe0..180ae1d10 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -560,10 +560,6 @@ module.exports = class AccountHelper {
 			}
 
 			const result = { access_token: accessToken, refresh_token: refreshToken, user }
-			const templateData = await notificationTemplateQueries.findOneEmailTemplate(
-				process.env.REGISTRATION_EMAIL_TEMPLATE_CODE,
-				user.organization_id
-			)
 
 			if (plaintextEmailId) {
 				notificationUtils.sendEmailNotification({
diff --git a/src/services/organization.js b/src/services/organization.js
index 377dc0ecf..01aa26790 100644
--- a/src/services/organization.js
+++ b/src/services/organization.js
@@ -56,6 +56,8 @@ module.exports = class OrganizationsHelper {
 							domain: domain,
 							organization_id: createdOrganization.id,
 							created_by: loggedInUserId,
+							updated_by: loggedInUserId,
+							tenant_code: bodyData.tenant_code,
 						}
 						await orgDomainQueries.create(domainCreationData)
 					})
diff --git a/src/services/user-sessions.js b/src/services/user-sessions.js
index 49d865efa..3569e6f23 100644
--- a/src/services/user-sessions.js
+++ b/src/services/user-sessions.js
@@ -41,7 +41,7 @@ module.exports = class UserSessionsHelper {
 			if (accessToken !== '') {
 				userSessionDetails.token = accessToken
 			}
-			if (accessToken !== '') {
+			if (refreshToken !== '') {
 				userSessionDetails.refresh_token = refreshToken
 			}
 

From 4cfd65514b046c2a188b25466110ffa00a814626 Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Tue, 12 Aug 2025 20:21:53 +0530
Subject: [PATCH 53/55] feat: Remove all 'admin' roles from
 user.user_organizations

---
 src/services/account.js | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/services/account.js b/src/services/account.js
index 984ae1fe0..8da41dcb0 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -753,8 +753,19 @@ module.exports = class AccountHelper {
 			const tenant_id =
 				tenantDetails && tenantDetails.parent_id !== null ? tenantDetails.parent_id : user.organization_id
  */
+
+			//Remove all 'admin' roles from user.user_organizations
+			if (user?.user_organizations?.length) {
+				user.user_organizations.forEach((org) => {
+					if (org.roles) {
+						org.roles = org.roles.filter((r) => r.role?.title?.toLowerCase() !== 'admin')
+					}
+				})
+			}
+
 			// Transform user data
 			user = UserTransformDTO.transform(user)
+
 			const tokenDetail = {
 				data: {
 					id: user.id,

From c238455ae603aab113e478a6be266dcd89bc817b Mon Sep 17 00:00:00 2001
From: Nevil Mathew <nevil@tunerlabs.com>
Date: Tue, 12 Aug 2025 23:10:31 +0530
Subject: [PATCH 54/55] removed: hardcoded admin value

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

diff --git a/src/services/account.js b/src/services/account.js
index 8da41dcb0..c33b9af7c 100644
--- a/src/services/account.js
+++ b/src/services/account.js
@@ -758,7 +758,7 @@ module.exports = class AccountHelper {
 			if (user?.user_organizations?.length) {
 				user.user_organizations.forEach((org) => {
 					if (org.roles) {
-						org.roles = org.roles.filter((r) => r.role?.title?.toLowerCase() !== 'admin')
+						org.roles = org.roles.filter((r) => r.role?.title?.toLowerCase() !== common.ADMIN_ROLE)
 					}
 				})
 			}

From 9303a85f8ebfcc6fe5381c977bd043739d943378 Mon Sep 17 00:00:00 2001
From: Rocky <rakesh.rockysgr@gmail.com>
Date: Wed, 13 Aug 2025 15:53:52 +0530
Subject: [PATCH 55/55] change status code

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

diff --git a/src/services/user.js b/src/services/user.js
index aabb87ee3..9c431ae42 100644
--- a/src/services/user.js
+++ b/src/services/user.js
@@ -323,7 +323,7 @@ module.exports = class UserHelper {
 				if (!user) {
 					return responses.failureResponse({
 						message: 'USER_NOT_FOUND',
-						statusCode: httpStatusCode.unauthorized,
+						statusCode: httpStatusCode.not_found,
 						responseCode: 'UNAUTHORIZED',
 					})
 				}
