Dealer_Onboarding_Backend/src/services/ParticipantService.ts

247 lines
9.1 KiB
TypeScript

import db from '../database/models/index.js';
import { ROLES, REQUEST_TYPES } from '../common/config/constants.js';
import { Op } from 'sequelize';
const {
RequestParticipant,
User,
TerminationRequest,
ConstitutionalChange,
Dealer,
Application,
District,
Region,
Zone
} = db;
export class ParticipantService {
/**
* Common helper to add a participant
*/
private static async addParticipant(requestId: string, requestType: string, userId: string, participantType: string = 'contributor', metadata: any = {}) {
try {
await RequestParticipant.findOrCreate({
where: {
requestId,
requestType,
userId
},
defaults: {
participantType,
joinedMethod: 'auto',
metadata: {
...metadata,
autoMapped: true,
assignedAt: new Date()
}
}
});
} catch (error) {
console.error(`Error adding participant ${userId} to ${requestType} ${requestId}:`, error);
}
}
/**
* Resolves location-based managers for a dealer
*/
private static async getDealerLocationManagers(dealerId: string) {
const dealer = await Dealer.findByPk(dealerId, {
include: [{
model: Application,
as: 'application',
include: [{
model: District,
as: 'district',
include: [
{ model: Region, as: 'region' },
{ model: Zone, as: 'zone' }
]
}]
}]
});
if (!dealer || !dealer.application || !dealer.application.district) {
return null;
}
const district = dealer.application.district;
return {
asmId: district.asmId,
zmId: district.zmId,
rbmId: district.region?.rbmId,
zbhId: district.zone?.zbhId
};
}
/**
* Assign participants for Termination Request
*/
static async assignTerminationParticipants(terminationId: string) {
try {
const termination = await TerminationRequest.findByPk(terminationId);
if (!termination) return;
const participantIds = new Set<string>();
// 0. The Dealer (Requester) should be a participant
if (termination.dealerId) {
// Find user account for this dealer
const dealerUser = await User.findOne({ where: { dealerId: termination.dealerId } });
if (dealerUser) participantIds.add(dealerUser.id);
}
// The Initiator (Admin who started termination)
if (termination.initiatedBy) participantIds.add(termination.initiatedBy);
// 1. Location based managers
const managers = await this.getDealerLocationManagers(termination.dealerId);
if (managers) {
if (managers.rbmId) participantIds.add(managers.rbmId);
if (managers.zbhId) participantIds.add(managers.zbhId);
}
// 2. National roles - Crucial for Termination Review
const nationalRoles = [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.CCO, ROLES.CEO, ROLES.LEGAL_ADMIN, ROLES.SUPER_ADMIN];
const nationalUsers = await User.findAll({
where: {
roleCode: { [Op.in]: nationalRoles },
status: 'active'
},
attributes: ['id']
});
nationalUsers.forEach((u: any) => participantIds.add(u.id));
// 3. Add all unique participants
let addedCount = 0;
for (const userId of participantIds) {
await this.addParticipant(termination.id, REQUEST_TYPES.TERMINATION, userId);
addedCount++;
}
console.log(`[ParticipantService] Added ${addedCount} participants to termination ${terminationId}`);
} catch (error) {
console.error('Error assigning termination participants:', error);
}
}
/**
* Assign participants for Constitutional Change Request
*/
static async assignConstitutionalParticipants(requestId: string) {
try {
const request = await ConstitutionalChange.findByPk(requestId);
if (!request) return;
const participantIds = new Set<string>();
// 0. The Dealer (Requester) should be a participant
if (request.dealerId) participantIds.add(request.dealerId);
// In ConstitutionalChange model, dealerId is the User ID
const user = await User.findByPk(request.dealerId);
if (user && user.dealerId) {
const managers = await this.getDealerLocationManagers(user.dealerId);
// 1. Location based managers
if (managers) {
if (managers.asmId) participantIds.add(managers.asmId);
if (managers.zmId) participantIds.add(managers.zmId);
if (managers.rbmId) participantIds.add(managers.rbmId);
if (managers.zbhId) participantIds.add(managers.zbhId);
}
}
// 2. National roles
const nationalRoles = [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.LEGAL_ADMIN, ROLES.SUPER_ADMIN];
const nationalUsers = await User.findAll({
where: {
roleCode: { [Op.in]: nationalRoles },
status: 'active'
},
attributes: ['id']
});
nationalUsers.forEach((u: any) => participantIds.add(u.id));
// 3. Add all unique participants
let addedCount = 0;
for (const userId of participantIds) {
await this.addParticipant(request.id, REQUEST_TYPES.CONSTITUTIONAL, userId);
addedCount++;
}
console.log(`[ParticipantService] Added ${addedCount} participants to constitutional change ${requestId}`);
} catch (error) {
console.error('Error assigning constitutional participants:', error);
}
}
/**
* Assign participants for Resignation Request
*/
static async assignResignationParticipants(requestId: string) {
try {
const resignation = await db.Resignation.findByPk(requestId);
if (!resignation) {
console.error(`[ParticipantService] Resignation not found: ${requestId}`);
return;
}
const participantIds = new Set<string>();
// 0. The Dealer themselves (Requester) should be a participant
if (resignation.dealerId) {
participantIds.add(resignation.dealerId);
}
// In Resignation model, dealerId is the User ID
const user = await User.findByPk(resignation.dealerId);
// 1. Try to get Location based managers if dealer profile exists
if (user && user.dealerId) {
const managers = await this.getDealerLocationManagers(user.dealerId);
if (managers) {
if (managers.asmId) participantIds.add(managers.asmId);
if (managers.rbmId) participantIds.add(managers.rbmId);
if (managers.zbhId) participantIds.add(managers.zbhId);
}
} else {
console.warn(`[ParticipantService] No Dealer Profile link found for user ${resignation.dealerId}. Only adding national roles.`);
}
// 2. National roles - Essential for workflow transparency
const nationalRoles = [
ROLES.DD_LEAD,
ROLES.NBH,
ROLES.DD_ADMIN,
ROLES.FINANCE,
ROLES.LEGAL_ADMIN,
ROLES.SUPER_ADMIN // Added Super Admin as observer
];
const nationalUsers = await User.findAll({
where: {
roleCode: { [Op.in]: nationalRoles },
status: 'active'
},
attributes: ['id']
});
nationalUsers.forEach((u: any) => participantIds.add(u.id));
// 3. Add all unique participants
let addedCount = 0;
for (const userId of participantIds) {
// Dealer gets 'owner' type, others get 'contributor'
const pType = userId === resignation.dealerId ? 'owner' : 'contributor';
await this.addParticipant(resignation.id, REQUEST_TYPES.RESIGNATION, userId, pType);
addedCount++;
}
console.log(`[ParticipantService] Added ${addedCount} participants to resignation ${requestId}`);
} catch (error) {
console.error('Error assigning resignation participants:', error);
}
}
}