zonal manager chnged to regions manged from districts managed need to recheck again end to end
This commit is contained in:
parent
c0a97854e0
commit
574fc84ba4
@ -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 ---');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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: [{
|
|
||||||
model: db.Role,
|
|
||||||
as: 'role',
|
|
||||||
where: { roleCode: { [Op.in]: ['ZM', 'DD-ZM', 'ZBH'] } }
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
model: db.District,
|
|
||||||
as: 'managedZmDistricts',
|
|
||||||
include: [
|
include: [
|
||||||
{ model: db.Zone, as: 'zone', attributes: ['id', 'name'] },
|
{
|
||||||
{ model: db.Region, as: 'region', attributes: ['id', 'name'] },
|
model: db.Role,
|
||||||
{ model: db.State, as: 'state', attributes: ['id', 'name'] }
|
as: 'role',
|
||||||
|
where: { roleCode: { [db.Sequelize.Op.in]: ['ZM', 'DD-ZM', 'ZBH'] } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: db.Region,
|
||||||
|
as: 'region',
|
||||||
|
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,34 +863,35 @@ 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
|
||||||
await db.UserRole.create({
|
if (Array.isArray(regionIds) && regionIds.length > 0) {
|
||||||
userId,
|
for (const regionId of regionIds) {
|
||||||
roleId: zmRole.id,
|
await db.UserRole.create({
|
||||||
zoneId: zoneId || null,
|
userId,
|
||||||
managerCode: zmCode || null,
|
roleId: zmRole.id,
|
||||||
isActive: true,
|
zoneId: zoneId || null,
|
||||||
isPrimary: true
|
regionId: regionId,
|
||||||
});
|
managerCode: zmCode || null,
|
||||||
|
isActive: true,
|
||||||
// Assign districts to this user if provided
|
isPrimary: true
|
||||||
// First, clear this ZM from any other districts they might have had
|
});
|
||||||
await db.District.update({ zmId: null, zmCode: null }, { where: { zmId: userId } });
|
}
|
||||||
|
} else {
|
||||||
if (Array.isArray(districts) && districts.length > 0) {
|
// Case with no specific region but assigned to a zone
|
||||||
// Then assign new ones
|
await db.UserRole.create({
|
||||||
const updateProps: any = {
|
userId,
|
||||||
zmId: userId,
|
roleId: zmRole.id,
|
||||||
zmCode: zmCode || null
|
zoneId: zoneId || null,
|
||||||
};
|
regionId: null,
|
||||||
if (zoneId) updateProps.zoneId = zoneId;
|
managerCode: zmCode || null,
|
||||||
|
isActive: true,
|
||||||
await db.District.update(
|
isPrimary: true
|
||||||
updateProps,
|
});
|
||||||
{ where: { id: { [db.Sequelize.Op.in]: districts } } }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
console.error('Save ZM error:', error);
|
console.error('Save ZM error:', 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;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user