diff --git a/scripts/seed-roles.ts b/scripts/seed-roles.ts index b3a36ba..0398352 100644 --- a/scripts/seed-roles.ts +++ b/scripts/seed-roles.ts @@ -23,7 +23,8 @@ const rolesToSeed = [ { roleCode: ROLES.CEO, roleName: 'CEO', category: 'LEADERSHIP', description: 'Chief Executive Officer' }, { roleCode: ROLES.SPARES_MANAGER, roleName: 'Spares Manager', category: 'DEPARTMENT', description: 'Spares Department Clearance' }, { roleCode: ROLES.SERVICE_MANAGER, roleName: 'Service Manager', category: 'DEPARTMENT', description: 'Service Department Clearance' }, - { roleCode: ROLES.ACCOUNTS_MANAGER, roleName: 'Accounts Manager', category: 'DEPARTMENT', description: 'Accounts Department Clearance' } + { roleCode: ROLES.ACCOUNTS_MANAGER, roleName: 'Accounts Manager', category: 'DEPARTMENT', description: 'Accounts Department Clearance' }, + { roleCode: ROLES.DD_AM, roleName: 'DD-AM', category: 'SALES', description: 'Dealer Development Area Manager' } ]; async function seedRoles() { diff --git a/scripts/seed_normalized_data.ts b/scripts/seed_normalized_data.ts index 66a5e8f..607a724 100644 --- a/scripts/seed_normalized_data.ts +++ b/scripts/seed_normalized_data.ts @@ -31,7 +31,8 @@ async function seed() { { roleCode: 'FDD', roleName: 'FDD Team', category: 'EXTERNAL' }, { roleCode: 'Legal Admin', roleName: 'Legal Admin', category: 'DEPARTMENT' }, { roleCode: 'CEO', roleName: 'CEO', category: 'NATIONAL' }, - { roleCode: 'CCO', roleName: 'CCO', category: 'NATIONAL' } + { roleCode: 'CCO', roleName: 'CCO', category: 'NATIONAL' }, + { roleCode: 'DD-AM', roleName: 'Dealer Development Area Manager', category: 'AREA' } ]; for (const r of roles) { @@ -139,7 +140,8 @@ async function seed() { { email: 'manav@royalenfield.com', name: 'manav', role: 'ZBH', zone: 'North Zone' }, { email: 'piyush@royalenfield.com', name: 'piyush', role: 'DD-ZM', zone: 'North Zone' }, { email: 'manish@royalenfield.com', name: 'manish', role: 'RBM', zone: 'North Zone' }, - { email: 'abhishek@royalenfield.com', name: 'abhishek', role: 'ASM', district: 'South Delhi' } + { email: 'abhishek@royalenfield.com', name: 'abhishek', role: 'ASM', district: 'South Delhi' }, + { email: 'bharghav@gmail.com', name: 'Bharghav', role: 'DD-AM', district: 'South Delhi' } ]; for (const m of frontendMocks) { const assignment: any = {}; diff --git a/src/modules/admin/admin.controller.ts b/src/modules/admin/admin.controller.ts index f54f973..3e81338 100644 --- a/src/modules/admin/admin.controller.ts +++ b/src/modules/admin/admin.controller.ts @@ -384,12 +384,12 @@ export const createUser = async (req: AuthRequest, res: Response) => { if (Array.isArray(assignments) && assignments.length > 0) { await upsertUserAssignments(user.id, assignments, req.user?.id); - } else if (districts && Array.isArray(districts) && roleCode === 'ASM') { - const targetRole = await Role.findOne({ where: { roleCode: 'ASM' } }); + } else if (districts && Array.isArray(districts) && ['ASM', 'DD-AM'].includes(roleCode)) { + const targetRole = await Role.findOne({ where: { roleCode } }); if (targetRole) { for (const distId of districts) { const sampleDistrict = await db.District.findByPk(distId); - const managerCode = await resolveManagerCode(targetRole.id, 'ASM', null); + const managerCode = await resolveManagerCode(targetRole.id, roleCode, null); await db.UserRole.create({ userId: user.id, roleId: targetRole.id, @@ -551,16 +551,20 @@ export const updateUser = async (req: AuthRequest, res: Response) => { if (Array.isArray(assignments)) { await upsertUserAssignments(id as string, assignments, req.user?.id); - } else if (districts && Array.isArray(districts) && roleCode === 'ASM') { - const targetRole = await Role.findOne({ where: { roleCode: 'ASM' } }); - if (!targetRole) throw new Error(`ASM role not found`); + } else if (districts && Array.isArray(districts) && ['ASM', 'DD-AM'].includes(roleCode)) { + const targetRole = await Role.findOne({ where: { roleCode } }); + if (!targetRole) throw new Error(`${roleCode} role not found`); await db.UserRole.destroy({ where: { userId: id, roleId: targetRole.id } }); - await db.District.update({ asmId: null, asmCode: null }, { where: { asmId: id } }); + if (roleCode === 'ASM') { + await db.District.update({ asmId: null, asmCode: null }, { where: { asmId: id } }); + } else { + await db.District.update({ ddAmId: null, ddAmCode: null }, { where: { ddAmId: id } }); + } for (const distId of districts) { const sampleDistrict = await db.District.findByPk(distId); - const managerCode = await resolveManagerCode(targetRole.id, 'ASM', null); + const managerCode = await resolveManagerCode(targetRole.id, roleCode, null); await db.UserRole.create({ userId: id, roleId: targetRole.id, diff --git a/src/modules/master/master.controller.ts b/src/modules/master/master.controller.ts index de17534..fc473c9 100644 --- a/src/modules/master/master.controller.ts +++ b/src/modules/master/master.controller.ts @@ -887,7 +887,7 @@ export const getASMs = async (req: Request, res: Response) => { try { const asms = await db.User.findAll({ where: { - roleCode: { [db.Sequelize.Op.in]: ['ASM', 'AREA SALES MANAGER'] }, + roleCode: { [db.Sequelize.Op.in]: ['ASM', 'AREA SALES MANAGER', 'DD-AM', ROLES.DD_AM] }, isActive: true }, include: [ @@ -896,7 +896,7 @@ export const getASMs = async (req: Request, res: Response) => { as: 'userRoles', where: { isActive: true }, required: false, - include: [{ model: db.Role, as: 'role', where: { roleCode: 'ASM' } }] + include: [{ model: db.Role, as: 'role', where: { roleCode: { [db.Sequelize.Op.in]: ['ASM', 'DD-AM', ROLES.DD_AM] } } }] }, { model: db.District, @@ -906,15 +906,29 @@ export const getASMs = async (req: Request, res: Response) => { { model: db.Region, as: 'region', attributes: ['id', 'name'] }, { model: db.Zone, as: 'zone', attributes: ['id', 'name'] } ] + }, + { + model: db.District, + as: 'managedDdAmDistricts', + include: [ + { model: db.State, as: 'state', attributes: ['id', 'name'] }, + { model: db.Region, as: 'region', attributes: ['id', 'name'] }, + { model: db.Zone, as: 'zone', attributes: ['id', 'name'] } + ] } ], order: [['fullName', 'ASC']] }); const result = (asms || []).map((u: any) => { - const districts = u.managedAsmDistricts || []; - const asmRoleAssignment = (u.userRoles || []).find((r: any) => r.role?.roleCode === 'ASM'); - const asmCode = asmRoleAssignment?.managerCode || u.employeeId; + const asmDistricts = u.managedAsmDistricts || []; + const ddAmDistricts = u.managedDdAmDistricts || []; + const districts = [...asmDistricts, ...ddAmDistricts]; + + const roleAssignment = (u.userRoles || []).find((r: any) => + ['ASM', 'DD-AM'].includes(r.role?.roleCode) + ); + const managerCode = roleAssignment?.managerCode || u.employeeId; const zoneSet = new Set(); const regionSet = new Set(); @@ -943,7 +957,8 @@ export const getASMs = async (req: Request, res: Response) => { email: u.email, phone: u.mobileNumber, employeeId: u.employeeId, - asmCode: asmCode || 'N/A', + asmCode: managerCode || 'N/A', + roleCode: u.roleCode, status: u.status, zoneId: zones[0]?.id || '', zoneName: zones[0]?.name || 'Unassigned', diff --git a/src/modules/onboarding/onboarding.controller.ts b/src/modules/onboarding/onboarding.controller.ts index e579f1c..cef1866 100644 --- a/src/modules/onboarding/onboarding.controller.ts +++ b/src/modules/onboarding/onboarding.controller.ts @@ -683,8 +683,13 @@ const assignStageEvaluators = async (appIdOrId: string) => { // --- INTERVIEWS --- // --- GROUND STAKEHOLDERS (Area / District Level) --- - // SRS §9.3.4: ASM is responsible for ground opportunity identification and field validation. - if (district.asmId) evaluatorMappings['DD'] = [{ id: district.asmId, role: 'ASM' }]; + // SRS §9.3.4: DD-AM is responsible for ground opportunity identification and field validation in onboarding. + // Falls back to ASM if DD-AM is not assigned for this district. + if (district.ddAmId) { + evaluatorMappings['DD'] = [{ id: district.ddAmId, role: 'DD-AM' }]; + } else if (district.asmId) { + evaluatorMappings['DD'] = [{ id: district.asmId, role: 'ASM' }]; + } // Level 1: DD-ZM (District manager) + RBM (Region manager) if (district.zmId) evaluatorMappings[1].push({ id: district.zmId, role: 'DD-ZM' });