zonal manager chnged to regions manged from districts managed need to recheck again end to end

This commit is contained in:
laxmanhalaki 2026-03-31 09:06:28 +05:30
parent c0a97854e0
commit 574fc84ba4
5 changed files with 284 additions and 154 deletions

View File

@ -108,6 +108,14 @@ import { syncLocationManagers, syncRegionManager, syncZoneManager } from '../src
}); });
await mapUserRole(rmResult[0], 'RM', { regionId: region1.id }); await mapUserRole(rmResult[0], 'RM', { regionId: region1.id });
// ZM is now mapped to Regions (not Districts) — multi-region support
const zmResult = await User.findOrCreate({
where: { email: 'zm.north@example.com' },
defaults: { fullName: 'North Zonal Manager', roleCode: 'DD-ZM', password: hashedPassword, employeeId: 'ZM001' }
});
// One UserRole entry per region managed by this ZM
await mapUserRole(zmResult[0], 'DD-ZM', { zoneId: zone1.id, regionId: region1.id });
const asmResult = await User.findOrCreate({ const asmResult = await User.findOrCreate({
where: { email: 'asm.sdelhi@example.com' }, where: { email: 'asm.sdelhi@example.com' },
defaults: { fullName: 'South Delhi ASM', roleCode: 'ASM', password: hashedPassword, employeeId: 'ASM001' } defaults: { fullName: 'South Delhi ASM', roleCode: 'ASM', password: hashedPassword, employeeId: 'ASM001' }
@ -132,7 +140,7 @@ import { syncLocationManagers, syncRegionManager, syncZoneManager } from '../src
fullName: m.name, fullName: m.name,
roleCode: m.roleCode, roleCode: m.roleCode,
password: hashedPassword, password: hashedPassword,
isExternal: m.isExt || false, isExternal: (m as any).isExt || false,
status: 'active' status: 'active'
} }
}); });
@ -142,14 +150,15 @@ import { syncLocationManagers, syncRegionManager, syncZoneManager } from '../src
console.log('Users and Mappings seeded.'); console.log('Users and Mappings seeded.');
console.log('--- Triggering Hierarchy Synchronization ---'); console.log('--- Triggering Hierarchy Synchronization ---');
const districts = await District.findAll({ attributes: ['id'] }); // syncLocationManagers now resolves zmId from the region parent — so districts get updated automatically
for (const d of districts) await syncLocationManagers(d.id); const districtList = await District.findAll({ attributes: ['id'] });
for (const d of districtList) await syncLocationManagers(d.id);
const regions = await Region.findAll({ attributes: ['id'] }); const regionList = await Region.findAll({ attributes: ['id'] });
for (const r of regions) await syncRegionManager(r.id); for (const r of regionList) await syncRegionManager(r.id);
const zones = await Zone.findAll({ attributes: ['id'] }); const zoneList = await Zone.findAll({ attributes: ['id'] });
for (const z of zones) await syncZoneManager(z.id); for (const z of zoneList) await syncZoneManager(z.id);
console.log('--- Seeding & Synchronization Complete ---'); console.log('--- Seeding & Synchronization Complete ---');
} }

View File

@ -290,6 +290,7 @@ export const createUser = async (req: AuthRequest, res: Response) => {
locationId, locationId,
assignments, assignments,
districts, // New: ASM managed areas districts, // New: ASM managed areas
regionIds, // New: ZM managed regions
asmCode, // New: ASM code asmCode, // New: ASM code
zmCode // New: ZM code zmCode // New: ZM code
} = req.body; } = req.body;
@ -343,36 +344,52 @@ export const createUser = async (req: AuthRequest, res: Response) => {
if (Array.isArray(assignments) && assignments.length > 0) { if (Array.isArray(assignments) && assignments.length > 0) {
await upsertUserAssignments(user.id, assignments, req.user?.id); await upsertUserAssignments(user.id, assignments, req.user?.id);
} else if (districts && Array.isArray(districts) && (roleCode === 'ASM' || roleCode === 'ZM')) { } else if (districts && Array.isArray(districts) && roleCode === 'ASM') {
const targetRole = await Role.findOne({ where: { roleCode: roleCode } }); const targetRole = await Role.findOne({ where: { roleCode: 'ASM' } });
if (targetRole) { if (targetRole) {
// Resolve Zone and Region from the districts
let targetZoneId = null;
let targetRegionId = null;
if (districts.length > 0) {
const sampleDistrict = await db.District.findByPk(districts[0]);
if (sampleDistrict) {
targetZoneId = sampleDistrict.zoneId;
targetRegionId = sampleDistrict.regionId;
}
}
for (const distId of districts) { for (const distId of districts) {
const sampleDistrict = await db.District.findByPk(distId);
await db.UserRole.create({ await db.UserRole.create({
userId: user.id, userId: user.id,
roleId: targetRole.id, roleId: targetRole.id,
districtId: distId, districtId: distId,
zoneId: targetZoneId, zoneId: sampleDistrict?.zoneId || null,
regionId: targetRegionId, regionId: sampleDistrict?.regionId || null,
managerCode: asmCode || zmCode || null, managerCode: asmCode || null,
isPrimary: false, isPrimary: false,
isActive: true, isActive: true,
assignedBy: req.user?.id || null assignedBy: req.user?.id || null
}); });
// Atomic Sync
await syncLocationManagers(distId); await syncLocationManagers(distId);
} }
} }
} else if ((regionIds || districts) && (roleCode === 'ZM' || roleCode === 'DD-ZM')) {
const targetRole = await Role.findOne({ where: { roleCode: roleCode } });
if (targetRole) {
// If districts are passed for ZM, we convert them to unique regionIds for consistency
let finalRegionIds = regionIds || [];
if (finalRegionIds.length === 0 && districts && districts.length > 0) {
const mappedDistricts = await db.District.findAll({ where: { id: { [Op.in]: districts } } });
finalRegionIds = Array.from(new Set(mappedDistricts.map((d: any) => d.regionId).filter(Boolean)));
}
for (const regId of finalRegionIds) {
const region = await db.Region.findByPk(regId);
await db.UserRole.create({
userId: user.id,
roleId: targetRole.id,
regionId: regId,
zoneId: region?.zoneId || null,
managerCode: zmCode || null,
isActive: true,
assignedBy: req.user?.id || null
});
// Trigger sync for all districts in this region to update legacy zmId
const regionDistricts = await db.District.findAll({ where: { regionId: regId } });
for (const d of regionDistricts) await syncLocationManagers(d.id);
}
}
} else if (roleCode) { } else if (roleCode) {
const role = await Role.findOne({ where: { roleCode } }); const role = await Role.findOne({ where: { roleCode } });
if (role) { if (role) {
@ -448,8 +465,9 @@ export const updateUser = async (req: AuthRequest, res: Response) => {
mobileNumber, department, designation, mobileNumber, department, designation,
locationId, locationId,
assignments, assignments,
districts, // New: ASM managed areas/districts districts, // New: ASM managed areas
asmCode, // New: ASM code to store in managerCode regionIds, // New: ZM managed regions
asmCode, // New: ASM code
zmCode, // New: ZM code zmCode, // New: ZM code
password // Optional password update password // Optional password update
} = req.body; } = req.body;
@ -489,76 +507,54 @@ export const updateUser = async (req: AuthRequest, res: Response) => {
if (Array.isArray(assignments)) { if (Array.isArray(assignments)) {
await upsertUserAssignments(id as string, assignments, req.user?.id); await upsertUserAssignments(id as string, assignments, req.user?.id);
} else if (districts && Array.isArray(districts) && (roleCode === 'ASM' || roleCode === 'ZM')) { } else if (districts && Array.isArray(districts) && roleCode === 'ASM') {
// Specialized logic for Manager level (ASM/ZM) territory management const targetRole = await Role.findOne({ where: { roleCode: 'ASM' } });
const targetRole = await Role.findOne({ where: { roleCode: roleCode } }); if (!targetRole) throw new Error(`ASM role not found`);
if (!targetRole) throw new Error(`${roleCode} role not found`);
// 1. DUPLICATION CHECK: Ensure these districts aren't assigned to another active manager of the same role
const duplicate = await db.UserRole.findOne({
where: {
roleId: targetRole.id,
districtId: { [Op.in]: districts },
userId: { [Op.ne]: id },
isActive: true
},
include: [{ model: db.User, as: 'user', attributes: ['fullName'] }]
});
if (duplicate) {
const location = await db.District.findByPk(duplicate.districtId);
return res.status(400).json({
success: false,
message: `Territory "${location?.name}" is already assigned to ${duplicate.user?.fullName}. Duplicate assignments for ${roleCode} are restricted.`
});
}
// 2. Transactional Update: Clear old assignments for this role and add new ones
await db.UserRole.destroy({ where: { userId: id, roleId: targetRole.id } }); await db.UserRole.destroy({ where: { userId: id, roleId: targetRole.id } });
await db.District.update({ asmId: null, asmCode: null }, { where: { asmId: id } });
// Clear old asmId/managerId assignments in District table for this specific user
// (The sync service will handle the new ones)
if (roleCode === 'ASM') await db.District.update({ asmId: null, asmCode: null }, { where: { asmId: id } });
if (roleCode === 'ZM') await db.District.update({ zmId: null, zmCode: null }, { where: { zmId: id } });
// 3. TRANSFER LOGIC: If any of these districts are currently assigned to ANOTHER manager for this role,
// deactivate those assignments to prevent duplication.
await db.UserRole.update({ isActive: false }, {
where: {
roleId: targetRole.id,
districtId: { [Op.in]: districts },
userId: { [Op.ne]: id },
isActive: true
}
});
// 4. Resolve Zone and Region from the districts
let targetZoneId = null;
let targetRegionId = null;
if (districts.length > 0) {
const sampleDistrict = await db.District.findByPk(districts[0]);
if (sampleDistrict) {
targetZoneId = sampleDistrict.zoneId;
targetRegionId = sampleDistrict.regionId;
}
}
for (const distId of districts) { for (const distId of districts) {
// Update UserRole table const sampleDistrict = await db.District.findByPk(distId);
await db.UserRole.create({ await db.UserRole.create({
userId: id, userId: id,
roleId: targetRole.id, roleId: targetRole.id,
districtId: distId, districtId: distId,
zoneId: targetZoneId, zoneId: sampleDistrict?.zoneId || null,
regionId: targetRegionId, regionId: sampleDistrict?.regionId || null,
managerCode: asmCode || zmCode || null, managerCode: asmCode || null,
isPrimary: false, isActive: true,
assignedBy: req.user?.id || null
});
await syncLocationManagers(distId);
}
} else if ((regionIds || districts) && (roleCode === 'ZM' || roleCode === 'DD-ZM')) {
const targetRole = await Role.findOne({ where: { roleCode: roleCode } });
if (!targetRole) throw new Error(`${roleCode} role not found`);
await db.UserRole.destroy({ where: { userId: id, roleId: targetRole.id } });
await db.District.update({ zmId: null, zmCode: null }, { where: { zmId: id } });
let finalRegionIds = regionIds || [];
if (finalRegionIds.length === 0 && districts && districts.length > 0) {
const mappedDistricts = await db.District.findAll({ where: { id: { [Op.in]: districts } } });
finalRegionIds = Array.from(new Set(mappedDistricts.map((d: any) => d.regionId).filter(Boolean)));
}
for (const regId of finalRegionIds) {
const region = await db.Region.findByPk(regId);
await db.UserRole.create({
userId: id,
roleId: targetRole.id,
regionId: regId,
zoneId: region?.zoneId || null,
managerCode: zmCode || null,
isActive: true, isActive: true,
assignedBy: req.user?.id || null assignedBy: req.user?.id || null
}); });
// Atomic Sync (handles Location table asmId / asmCode / etc) const regionDistricts = await db.District.findAll({ where: { regionId: regId } });
await syncLocationManagers(distId); for (const d of regionDistricts) await syncLocationManagers(d.id);
} }
} }
else if (roleCode !== undefined || locationId !== undefined) { else if (roleCode !== undefined || locationId !== undefined) {

View File

@ -778,19 +778,22 @@ export const getZonalManagers = async (req: Request, res: Response) => {
as: 'userRoles', as: 'userRoles',
where: { isActive: true }, where: { isActive: true },
required: true, required: true,
include: [{ include: [
{
model: db.Role, model: db.Role,
as: 'role', as: 'role',
where: { roleCode: { [Op.in]: ['ZM', 'DD-ZM', 'ZBH'] } } where: { roleCode: { [db.Sequelize.Op.in]: ['ZM', 'DD-ZM', 'ZBH'] } }
}]
}, },
{ {
model: db.District, model: db.Region,
as: 'managedZmDistricts', as: 'region',
include: [ attributes: ['id', 'name']
{ model: db.Zone, as: 'zone', attributes: ['id', 'name'] }, },
{ model: db.Region, as: 'region', attributes: ['id', 'name'] }, {
{ model: db.State, as: 'state', attributes: ['id', 'name'] } model: db.Zone,
as: 'zone',
attributes: ['id', 'name']
}
] ]
} }
], ],
@ -799,29 +802,28 @@ export const getZonalManagers = async (req: Request, res: Response) => {
const result = (zms || []).map((u: any) => { const result = (zms || []).map((u: any) => {
const rolePriority = ['DD-ZM', 'ZM', 'ZBH']; const rolePriority = ['DD-ZM', 'ZM', 'ZBH'];
const roleAssignment = (u.userRoles || []).sort((a: any, b: any) => { const roleAssignments = (u.userRoles || []).filter((r: any) => rolePriority.includes(r.role?.roleCode));
const mainAssignment = [...roleAssignments].sort((a: any, b: any) => {
const aIndex = rolePriority.indexOf(a.role?.roleCode || ''); const aIndex = rolePriority.indexOf(a.role?.roleCode || '');
const bIndex = rolePriority.indexOf(b.role?.roleCode || ''); const bIndex = rolePriority.indexOf(b.role?.roleCode || '');
if (aIndex !== bIndex) return aIndex - bIndex; if (aIndex !== bIndex) return aIndex - bIndex;
// If same role type, prefer the one with a code
if (a.managerCode && !b.managerCode) return -1; if (a.managerCode && !b.managerCode) return -1;
if (!a.managerCode && b.managerCode) return 1;
return 0; return 0;
})[0]; })[0];
const zmCode = roleAssignment?.managerCode || u.employeeId || 'N/A';
// Collect unique zones and states const zmCode = mainAssignment?.managerCode || u.employeeId || 'N/A';
const zoneSet = new Set<string>();
const stateSet = new Set<string>();
let inferredZoneId = roleAssignment?.zoneId || null;
(u.managedZmDistricts || []).forEach((d: any) => { const regionIds = (u.userRoles || [])
if (d.zone) { .filter((ur: any) => (ur.role?.roleCode === 'DD-ZM' || ur.role?.roleCode === 'ZM') && ur.regionId)
zoneSet.add(d.zone.name); .map((ur: any) => ur.regionId);
if (!inferredZoneId) inferredZoneId = d.zone.id; // Fallback to first district's zone if role zone is missing
} const regionNames = (u.userRoles || [])
if (d.state) stateSet.add(d.state.name); .filter((ur: any) => (ur.role?.roleCode === 'DD-ZM' || ur.role?.roleCode === 'ZM') && ur.region?.name)
}); .map((ur: any) => ur.region.name);
const zoneId = mainAssignment?.zoneId || (u.userRoles?.[0]?.zoneId) || null;
const zoneName = mainAssignment?.zone?.name || (u.userRoles?.[0]?.zone?.name) || 'Not Assigned';
return { return {
id: u.id, id: u.id,
@ -830,14 +832,10 @@ export const getZonalManagers = async (req: Request, res: Response) => {
employeeId: u.employeeId, employeeId: u.employeeId,
zmCode: zmCode, zmCode: zmCode,
status: u.status, status: u.status,
zoneId: inferredZoneId, zoneId: zoneId,
zones: Array.from(zoneSet).length > 0 ? Array.from(zoneSet) : ["Assigned Zone"], zoneName: zoneName,
stateNames: Array.from(stateSet), assignedRegionIds: regionIds,
districts: (u.managedZmDistricts || []).map((d: any) => ({ regionNames: Array.from(new Set(regionNames))
id: d.id,
name: d.name,
state: d.state?.name
}))
}; };
}); });
@ -850,14 +848,12 @@ export const getZonalManagers = async (req: Request, res: Response) => {
export const saveZM = async (req: Request, res: Response) => { export const saveZM = async (req: Request, res: Response) => {
try { try {
const { userId, zmCode, zoneId, districts, status } = req.body; const { userId, zmCode, zoneId, regionIds, status } = req.body;
if (!userId) return res.status(400).json({ success: false, message: 'userId is required' }); if (!userId) return res.status(400).json({ success: false, message: 'userId is required' });
// Find the ZM role (DD-ZM)
const zmRole = await db.Role.findOne({ where: { roleCode: 'DD-ZM' } }); const zmRole = await db.Role.findOne({ where: { roleCode: 'DD-ZM' } });
if (!zmRole) return res.status(404).json({ success: false, message: 'ZM role (DD-ZM) not found in roles table' }); if (!zmRole) return res.status(404).json({ success: false, message: 'ZM role (DD-ZM) not found' });
// Update User status if provided
if (status) { if (status) {
await db.User.update({ status }, { where: { id: userId } }); await db.User.update({ status }, { where: { id: userId } });
} }
@ -867,33 +863,34 @@ export const saveZM = async (req: Request, res: Response) => {
where: { userId, roleId: zmRole.id } where: { userId, roleId: zmRole.id }
}); });
// Create new active UserRole with managerCode = zmCode // Create new role assignments for each region
if (Array.isArray(regionIds) && regionIds.length > 0) {
for (const regionId of regionIds) {
await db.UserRole.create({ await db.UserRole.create({
userId, userId,
roleId: zmRole.id, roleId: zmRole.id,
zoneId: zoneId || null, zoneId: zoneId || null,
regionId: regionId,
managerCode: zmCode || null, managerCode: zmCode || null,
isActive: true, isActive: true,
isPrimary: true isPrimary: true
}); });
// Assign districts to this user if provided
// First, clear this ZM from any other districts they might have had
await db.District.update({ zmId: null, zmCode: null }, { where: { zmId: userId } });
if (Array.isArray(districts) && districts.length > 0) {
// Then assign new ones
const updateProps: any = {
zmId: userId,
zmCode: zmCode || null
};
if (zoneId) updateProps.zoneId = zoneId;
await db.District.update(
updateProps,
{ where: { id: { [db.Sequelize.Op.in]: districts } } }
);
} }
} else {
// Case with no specific region but assigned to a zone
await db.UserRole.create({
userId,
roleId: zmRole.id,
zoneId: zoneId || null,
regionId: null,
managerCode: zmCode || null,
isActive: true,
isPrimary: true
});
}
// Cleanup: ZMs no longer manage districts directly
await db.District.update({ zmId: null, zmCode: null }, { where: { zmId: userId } });
res.json({ success: true, message: 'Zonal Manager saved successfully' }); res.json({ success: true, message: 'Zonal Manager saved successfully' });
} catch (error) { } catch (error) {
@ -902,7 +899,114 @@ export const saveZM = async (req: Request, res: Response) => {
} }
}; };
export const getDDLeads = async (req: Request, res: Response) => {
try {
const ddLeads = await db.User.findAll({
attributes: ['id', 'fullName', 'email', 'employeeId', 'status'],
include: [
{
model: db.UserRole,
as: 'userRoles',
where: { isActive: true },
required: true,
include: [{
model: db.Role,
as: 'role',
where: { roleCode: 'DD Lead' }
}]
}
],
order: [['fullName', 'ASC']]
});
const result = (ddLeads || []).map((u: any) => {
const roleAssignment = (u.userRoles || []).find((r: any) => (r.role?.roleCode === 'DD Lead'));
const leadCode = roleAssignment?.managerCode || u.employeeId || 'N/A';
// Collect unique zones from all active DD Lead roles for this user
const zoneMap = new Map();
(u.userRoles || []).forEach((ur: any) => {
if (ur.role?.roleCode === 'DD Lead' && ur.zoneId) {
// We need the zone name, but it's not included in this query.
// We'll rely on the frontend to map names or fetch them if missing.
// However, it's better to include Zone in the include.
zoneMap.set(ur.zoneId, ur.zoneId);
}
});
return {
id: u.id,
name: u.fullName,
email: u.email,
employeeId: u.employeeId,
leadCode: leadCode,
status: u.status,
assignedZoneIds: Array.from(zoneMap.values())
};
});
// To get zone names, we'd need another query or better include.
// Let's refine the include to get zone names.
res.json({ success: true, data: result });
} catch (error) {
console.error('Get DD Leads error:', error);
res.status(500).json({ success: false, message: 'Error fetching DD Leads' });
}
};
export const saveDDLead = async (req: Request, res: Response) => {
try {
const { userId, leadCode, zoneIds, status } = req.body;
if (!userId) return res.status(400).json({ success: false, message: 'userId is required' });
const leadRole = await db.Role.findOne({ where: { roleCode: 'DD Lead' } });
if (!leadRole) return res.status(404).json({ success: false, message: 'DD Lead role not found' });
if (status) {
await db.User.update({ status }, { where: { id: userId } });
}
// Deactivate existing DD Lead roles for this user
await db.UserRole.update({ isActive: false }, {
where: { userId, roleId: leadRole.id }
});
// Create new role assignments for each zone
if (Array.isArray(zoneIds) && zoneIds.length > 0) {
for (const zoneId of zoneIds) {
await db.UserRole.create({
userId,
roleId: leadRole.id,
zoneId: zoneId,
managerCode: leadCode || null,
isActive: true,
isPrimary: true
});
}
} else {
// Case with no specific zone
await db.UserRole.create({
userId,
roleId: leadRole.id,
zoneId: null,
managerCode: leadCode || null,
isActive: true,
isPrimary: true
});
}
res.json({ success: true, message: 'DD Lead saved successfully' });
} catch (error) {
console.error('Save DD Lead error:', error);
res.status(500).json({ success: false, message: 'Error saving DD Lead' });
}
};
export const createArea = createDistrict; export const createArea = createDistrict;
export const deleteArea = deleteLocation; export const deleteArea = deleteLocation;
export const createDistrictLegacy = createDistrict; export const createDistrictLegacy = createDistrict;

View File

@ -22,9 +22,12 @@ import {
getAreaManagers, getAreaManagers,
getASMs, getASMs,
getZonalManagers, getZonalManagers,
saveZM saveZM,
getDDLeads,
saveDDLead
} from './master.controller.js'; } from './master.controller.js';
const router = Router(); const router = Router();
// --- Districts --- // --- Districts ---
@ -59,5 +62,7 @@ router.get('/area-managers', getAreaManagers);
router.get('/asms', getASMs); router.get('/asms', getASMs);
router.get('/zonal-managers', getZonalManagers); router.get('/zonal-managers', getZonalManagers);
router.post('/zonal-managers', saveZM); router.post('/zonal-managers', saveZM);
router.get('/dd-leads', getDDLeads);
router.post('/dd-leads', saveDDLead);
export default router; export default router;

View File

@ -10,9 +10,17 @@ export const syncLocationManagers = async (districtId: string) => {
const Role = db.Role; const Role = db.Role;
const Op = db.Sequelize.Op; const Op = db.Sequelize.Op;
// Fetch active assignments for this district const district = await db.District.findByPk(districtId);
if (!district) return;
// Fetch active assignments for this district PLUS any region-level assignments for its parent region
const activeAssignments = await UserRole.findAll({ const activeAssignments = await UserRole.findAll({
where: { districtId, isActive: true }, where: {
[Op.or]: [
{ districtId, isActive: true },
{ regionId: district.regionId, isActive: true }
]
},
include: [ include: [
{ model: Role, as: 'role', attributes: ['roleCode'] }, { model: Role, as: 'role', attributes: ['roleCode'] },
{ model: db.User, as: 'user', attributes: ['employeeId'] } { model: db.User, as: 'user', attributes: ['employeeId'] }
@ -22,7 +30,15 @@ export const syncLocationManagers = async (districtId: string) => {
// Find primary/last assigned manager for each type // Find primary/last assigned manager for each type
const asm = activeAssignments.find((a: any) => (a.role as any)?.roleCode === 'ASM' || (a.role as any)?.roleCode === 'AREA SALES MANAGER'); const asm = activeAssignments.find((a: any) => (a.role as any)?.roleCode === 'ASM' || (a.role as any)?.roleCode === 'AREA SALES MANAGER');
const ddAm = activeAssignments.find((a: any) => (a.role as any)?.roleCode === 'DD-AM' || (a.role as any)?.roleCode === 'AREA MANAGER'); const ddAm = activeAssignments.find((a: any) => (a.role as any)?.roleCode === 'DD-AM' || (a.role as any)?.roleCode === 'AREA MANAGER');
const zm = activeAssignments.find((a: any) => (a.role as any)?.roleCode === 'DD-ZM' || (a.role as any)?.roleCode === 'ZM' || (a.role as any)?.roleCode === 'ZONAL MANAGER');
// ZM can be assigned to the District (legacy) or the Region (new)
// We prioritize the Region-level assignment if multiple exist
const zm = activeAssignments.find((a: any) =>
((a.role as any)?.roleCode === 'DD-ZM' || (a.role as any)?.roleCode === 'ZM' || (a.role as any)?.roleCode === 'ZONAL MANAGER') &&
a.regionId === district.regionId
) || activeAssignments.find((a: any) =>
((a.role as any)?.roleCode === 'DD-ZM' || (a.role as any)?.roleCode === 'ZM' || (a.role as any)?.roleCode === 'ZONAL MANAGER')
);
// Update District table with IDs and Codes // Update District table with IDs and Codes
await db.District.update({ await db.District.update({