end to end flow testing for all modules paralley enhancing profile schema to gather all info
This commit is contained in:
parent
934fd7a907
commit
19c766c999
@ -1,22 +1,41 @@
|
|||||||
|
|
||||||
|
import pkg from 'pg';
|
||||||
|
const { Client } = pkg;
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import db from './src/database/models/index.js';
|
|
||||||
const { RequestParticipant } = (db as any).default || db;
|
|
||||||
|
|
||||||
const applicationId = '6139d6f9-f3c1-4e55-903b-3516d3a08955';
|
async function check() {
|
||||||
|
const client = new Client({
|
||||||
async function checkParticipants() {
|
user: process.env.DB_USER || 'postgres',
|
||||||
try {
|
password: process.env.DB_PASSWORD || 'Admin@123',
|
||||||
const count = await RequestParticipant.count({
|
host: process.env.DB_HOST || 'localhost',
|
||||||
where: { requestId: applicationId, requestType: 'application' }
|
database: process.env.DB_NAME || 'royal_enfield_onboarding',
|
||||||
|
port: parseInt(process.env.DB_PORT || '5432'),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Application has ${count} participants.`);
|
try {
|
||||||
|
await client.connect();
|
||||||
|
const res = await client.query('SELECT * FROM request_participants WHERE "requestType" = $1', ['resignation']);
|
||||||
|
console.log(`Found ${res.rows.length} participants for Resignations.`);
|
||||||
|
|
||||||
} catch (error) {
|
const termRes = await client.query('SELECT * FROM request_participants WHERE "requestType" = $1', ['termination']);
|
||||||
console.error('Error checking participants:', error);
|
console.log(`Found ${termRes.rows.length} participants for Terminations.`);
|
||||||
|
|
||||||
|
if (res.rows.length > 0) {
|
||||||
|
console.log('Sample Resignation Participant:', JSON.stringify(res.rows[0], null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (termRes.rows.length > 0) {
|
||||||
|
console.log('Sample Termination Participant:', JSON.stringify(termRes.rows[0], null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
const resignations = await client.query('SELECT id, "resignationId" FROM resignations LIMIT 5');
|
||||||
|
console.log('Resignations in DB:', resignations.rows.map(r => r.resignationId));
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error:', err.message);
|
||||||
} finally {
|
} finally {
|
||||||
process.exit();
|
await client.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkParticipants();
|
check();
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
import { ParticipantService } from './src/services/ParticipantService.js';
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
try {
|
|
||||||
const requestId = '29b742a7-6d9f-4736-8aae-295ffe32ef75';
|
|
||||||
console.log(`Fixing participants for resignation ${requestId}...`);
|
|
||||||
|
|
||||||
const { Resignation, User, Dealer, Application, District } = (await import('./src/database/models/index.js')).default;
|
|
||||||
const resignation = await Resignation.findByPk(requestId);
|
|
||||||
console.log('Resignation Record:', JSON.stringify(resignation, null, 2));
|
|
||||||
|
|
||||||
if (resignation) {
|
|
||||||
const user = await User.findByPk(resignation.dealerId);
|
|
||||||
console.log('User Record:', JSON.stringify(user, null, 2));
|
|
||||||
if (user && user.dealerId) {
|
|
||||||
const dealer = await Dealer.findByPk(user.dealerId, {
|
|
||||||
include: [{ model: Application, as: 'application', include: [{ model: District, as: 'district' }] }]
|
|
||||||
});
|
|
||||||
console.log('Dealer/Application/District Record:', JSON.stringify(dealer, null, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await ParticipantService.assignResignationParticipants(requestId);
|
|
||||||
console.log('Done.');
|
|
||||||
process.exit(0);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fixing participants:', error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
@ -271,7 +271,8 @@ export default (sequelize: Sequelize) => {
|
|||||||
Application.hasMany(models.RequestParticipant, {
|
Application.hasMany(models.RequestParticipant, {
|
||||||
foreignKey: 'requestId',
|
foreignKey: 'requestId',
|
||||||
as: 'participants',
|
as: 'participants',
|
||||||
scope: { requestType: 'application' }
|
scope: { requestType: 'application' },
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
Application.hasOne(models.DealerCode, { foreignKey: 'applicationId', as: 'dealerCode' });
|
Application.hasOne(models.DealerCode, { foreignKey: 'applicationId', as: 'dealerCode' });
|
||||||
Application.hasMany(models.StageApprovalAction, { foreignKey: 'applicationId', as: 'stageApprovals' });
|
Application.hasMany(models.StageApprovalAction, { foreignKey: 'applicationId', as: 'stageApprovals' });
|
||||||
@ -279,6 +280,8 @@ export default (sequelize: Sequelize) => {
|
|||||||
Application.hasMany(models.SecurityDeposit, { foreignKey: 'applicationId', as: 'securityDeposits' });
|
Application.hasMany(models.SecurityDeposit, { foreignKey: 'applicationId', as: 'securityDeposits' });
|
||||||
Application.hasOne(models.EorChecklist, { foreignKey: 'applicationId', as: 'eorChecklist' });
|
Application.hasOne(models.EorChecklist, { foreignKey: 'applicationId', as: 'eorChecklist' });
|
||||||
Application.hasMany(models.FddAssignment, { foreignKey: 'applicationId', as: 'fddAssignments' });
|
Application.hasMany(models.FddAssignment, { foreignKey: 'applicationId', as: 'fddAssignments' });
|
||||||
|
Application.hasMany(models.LoiRequest, { foreignKey: 'applicationId', as: 'loiRequests' });
|
||||||
|
Application.hasMany(models.LoaRequest, { foreignKey: 'applicationId', as: 'loaRequests' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return Application;
|
return Application;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Model, DataTypes, Sequelize } from 'sequelize';
|
|||||||
|
|
||||||
export interface DealerAttributes {
|
export interface DealerAttributes {
|
||||||
id: string;
|
id: string;
|
||||||
applicationId: string;
|
applicationId: string | null;
|
||||||
dealerCodeId: string | null;
|
dealerCodeId: string | null;
|
||||||
legalName: string;
|
legalName: string;
|
||||||
businessName: string;
|
businessName: string;
|
||||||
@ -12,6 +12,13 @@ export interface DealerAttributes {
|
|||||||
panNumber: string | null;
|
panNumber: string | null;
|
||||||
status: string;
|
status: string;
|
||||||
onboardedAt: Date | null;
|
onboardedAt: Date | null;
|
||||||
|
loiDate: Date | null;
|
||||||
|
loaDate: Date | null;
|
||||||
|
isLegacy: boolean;
|
||||||
|
securityDepositAmount: number | null;
|
||||||
|
securityDepositDate: Date | null;
|
||||||
|
lastWorkingDay: Date | null;
|
||||||
|
exitReason: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DealerInstance extends Model<DealerAttributes>, DealerAttributes { }
|
export interface DealerInstance extends Model<DealerAttributes>, DealerAttributes { }
|
||||||
@ -25,7 +32,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
},
|
},
|
||||||
applicationId: {
|
applicationId: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: true,
|
||||||
references: {
|
references: {
|
||||||
model: 'applications',
|
model: 'applications',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
@ -70,6 +77,34 @@ export default (sequelize: Sequelize) => {
|
|||||||
onboardedAt: {
|
onboardedAt: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: true
|
allowNull: true
|
||||||
|
},
|
||||||
|
loiDate: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
loaDate: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
isLegacy: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
|
securityDepositAmount: {
|
||||||
|
type: DataTypes.DECIMAL(15, 2),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
securityDepositDate: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
lastWorkingDay: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
exitReason: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'dealers',
|
tableName: 'dealers',
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export interface FddReportAttributes {
|
|||||||
recommendation: string | null;
|
recommendation: string | null;
|
||||||
verifiedAt: Date | null;
|
verifiedAt: Date | null;
|
||||||
verifiedBy: string | null;
|
verifiedBy: string | null;
|
||||||
|
submittedBy: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FddReportInstance extends Model<FddReportAttributes>, FddReportAttributes { }
|
export interface FddReportInstance extends Model<FddReportAttributes>, FddReportAttributes { }
|
||||||
@ -54,6 +55,14 @@ export default (sequelize: Sequelize) => {
|
|||||||
model: 'users',
|
model: 'users',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
submittedBy: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'fdd_reports',
|
tableName: 'fdd_reports',
|
||||||
@ -64,6 +73,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
FddReport.belongsTo(models.FddAssignment, { foreignKey: 'assignmentId', as: 'assignment' });
|
FddReport.belongsTo(models.FddAssignment, { foreignKey: 'assignmentId', as: 'assignment' });
|
||||||
FddReport.belongsTo(models.OnboardingDocument, { foreignKey: 'reportDocumentId', as: 'reportDocument' });
|
FddReport.belongsTo(models.OnboardingDocument, { foreignKey: 'reportDocumentId', as: 'reportDocument' });
|
||||||
FddReport.belongsTo(models.User, { foreignKey: 'verifiedBy', as: 'verifier' });
|
FddReport.belongsTo(models.User, { foreignKey: 'verifiedBy', as: 'verifier' });
|
||||||
|
FddReport.belongsTo(models.User, { foreignKey: 'submittedBy', as: 'submitter' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return FddReport;
|
return FddReport;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Model, DataTypes, Sequelize } from 'sequelize';
|
|||||||
|
|
||||||
export interface LoaRequestAttributes {
|
export interface LoaRequestAttributes {
|
||||||
id: string;
|
id: string;
|
||||||
applicationId: string;
|
applicationId: string | null;
|
||||||
status: string;
|
status: string;
|
||||||
requestedBy: string | null;
|
requestedBy: string | null;
|
||||||
approvedAt: Date | null;
|
approvedAt: Date | null;
|
||||||
@ -20,7 +20,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
},
|
},
|
||||||
applicationId: {
|
applicationId: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: true,
|
||||||
references: {
|
references: {
|
||||||
model: 'applications',
|
model: 'applications',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Model, DataTypes, Sequelize } from 'sequelize';
|
|||||||
|
|
||||||
export interface LoiRequestAttributes {
|
export interface LoiRequestAttributes {
|
||||||
id: string;
|
id: string;
|
||||||
applicationId: string;
|
applicationId: string | null;
|
||||||
status: string;
|
status: string;
|
||||||
requestedBy: string | null;
|
requestedBy: string | null;
|
||||||
approvedAt: Date | null;
|
approvedAt: Date | null;
|
||||||
@ -20,7 +20,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
},
|
},
|
||||||
applicationId: {
|
applicationId: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: true,
|
||||||
references: {
|
references: {
|
||||||
model: 'applications',
|
model: 'applications',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
|
|||||||
@ -150,7 +150,8 @@ export default (sequelize: Sequelize) => {
|
|||||||
Resignation.hasMany(models.RequestParticipant, {
|
Resignation.hasMany(models.RequestParticipant, {
|
||||||
foreignKey: 'requestId',
|
foreignKey: 'requestId',
|
||||||
as: 'participants',
|
as: 'participants',
|
||||||
scope: { requestType: 'resignation' }
|
scope: { requestType: 'resignation' },
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import { Model, DataTypes, Sequelize } from 'sequelize';
|
|||||||
|
|
||||||
export interface SecurityDepositAttributes {
|
export interface SecurityDepositAttributes {
|
||||||
id: string;
|
id: string;
|
||||||
applicationId: string;
|
applicationId: string | null;
|
||||||
amount: number;
|
amount: number;
|
||||||
paymentReference: string | null;
|
paymentReference: string | null;
|
||||||
proofDocumentId: string | null;
|
proofDocumentId: string | null;
|
||||||
status: string;
|
status: string;
|
||||||
depositType: 'INITIAL' | 'FINAL';
|
depositType: 'SECURITY_DEPOSIT' | 'FIRST_FILL';
|
||||||
verifiedAt: Date | null;
|
verifiedAt: Date | null;
|
||||||
verifiedBy: string | null;
|
verifiedBy: string | null;
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
},
|
},
|
||||||
applicationId: {
|
applicationId: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: true,
|
||||||
references: {
|
references: {
|
||||||
model: 'applications',
|
model: 'applications',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
@ -50,9 +50,9 @@ export default (sequelize: Sequelize) => {
|
|||||||
defaultValue: 'pending'
|
defaultValue: 'pending'
|
||||||
},
|
},
|
||||||
depositType: {
|
depositType: {
|
||||||
type: DataTypes.ENUM('INITIAL', 'FINAL'),
|
type: DataTypes.ENUM('SECURITY_DEPOSIT', 'FIRST_FILL'),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: 'INITIAL'
|
defaultValue: 'SECURITY_DEPOSIT'
|
||||||
},
|
},
|
||||||
verifiedAt: {
|
verifiedAt: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
|
|||||||
@ -114,7 +114,8 @@ export default (sequelize: Sequelize) => {
|
|||||||
TerminationRequest.hasMany(models.RequestParticipant, {
|
TerminationRequest.hasMany(models.RequestParticipant, {
|
||||||
foreignKey: 'requestId',
|
foreignKey: 'requestId',
|
||||||
as: 'participants',
|
as: 'participants',
|
||||||
scope: { requestType: 'termination' }
|
scope: { requestType: 'termination' },
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -150,14 +150,24 @@ const processStageDecision = async (params: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let report = await db.FddReport.findOne({ where: { assignmentId: assignment.id } });
|
||||||
|
|
||||||
|
if (report) {
|
||||||
|
await report.update({
|
||||||
|
verifiedAt: new Date(),
|
||||||
|
verifiedBy: userId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
await db.FddReport.create({
|
await db.FddReport.create({
|
||||||
assignmentId: assignment.id,
|
assignmentId: assignment.id,
|
||||||
reportDocumentId: lastReportDoc?.id || null,
|
reportDocumentId: lastReportDoc?.id || null,
|
||||||
findings,
|
findings,
|
||||||
recommendation,
|
recommendation,
|
||||||
verifiedAt: new Date(),
|
verifiedAt: new Date(),
|
||||||
verifiedBy: userId
|
verifiedBy: userId,
|
||||||
|
submittedBy: userId // Admin submitted it if no existing report
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await assignment.update({ status: 'Report Submitted' });
|
await assignment.update({ status: 'Report Submitted' });
|
||||||
|
|
||||||
|
|||||||
@ -97,6 +97,24 @@ export const createDealer = async (req: AuthRequest, res: Response) => {
|
|||||||
}, { where: { id: targetDealerCodeId } });
|
}, { where: { id: targetDealerCodeId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch milestone dates and payments from requests
|
||||||
|
const loiRequest = await db.LoiRequest.findOne({
|
||||||
|
where: { applicationId: application.id, status: { [Op.iLike]: 'Approved' } },
|
||||||
|
order: [['approvedAt', 'DESC']]
|
||||||
|
});
|
||||||
|
const loaRequest = await db.LoaRequest.findOne({
|
||||||
|
where: { applicationId: application.id, status: { [Op.iLike]: 'Approved' } },
|
||||||
|
order: [['approvedAt', 'DESC']]
|
||||||
|
});
|
||||||
|
const deposit = await db.SecurityDeposit.findOne({
|
||||||
|
where: {
|
||||||
|
applicationId: application.id,
|
||||||
|
status: 'Verified',
|
||||||
|
depositType: 'SECURITY_DEPOSIT' // Strictly the Security Deposit
|
||||||
|
},
|
||||||
|
order: [['verifiedAt', 'DESC']]
|
||||||
|
});
|
||||||
|
|
||||||
// Create Dealer Profile
|
// Create Dealer Profile
|
||||||
dealer = await Dealer.create({
|
dealer = await Dealer.create({
|
||||||
applicationId,
|
applicationId,
|
||||||
@ -105,7 +123,11 @@ export const createDealer = async (req: AuthRequest, res: Response) => {
|
|||||||
businessName: application.applicantName,
|
businessName: application.applicantName,
|
||||||
constitutionType: application.constitutionType || 'Proprietorship',
|
constitutionType: application.constitutionType || 'Proprietorship',
|
||||||
status: 'Active',
|
status: 'Active',
|
||||||
onboardedAt: new Date()
|
onboardedAt: new Date(),
|
||||||
|
loiDate: loiRequest?.approvedAt,
|
||||||
|
loaDate: loaRequest?.approvedAt,
|
||||||
|
securityDepositAmount: deposit?.amount,
|
||||||
|
securityDepositDate: deposit?.verifiedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
await AuditLog.create({
|
await AuditLog.create({
|
||||||
|
|||||||
@ -23,7 +23,14 @@ export const getAssignment = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const assignment = await FddAssignment.findOne({
|
const assignment = await FddAssignment.findOne({
|
||||||
where: { applicationId: application.id },
|
where: { applicationId: application.id },
|
||||||
include: [{ model: FddReport, as: 'reports' }]
|
include: [{
|
||||||
|
model: FddReport,
|
||||||
|
as: 'reports',
|
||||||
|
include: [
|
||||||
|
{ model: db.User, as: 'submitter', attributes: ['fullName'] },
|
||||||
|
{ model: db.User, as: 'verifier', attributes: ['fullName'] }
|
||||||
|
]
|
||||||
|
}]
|
||||||
});
|
});
|
||||||
res.json({ success: true, data: assignment });
|
res.json({ success: true, data: assignment });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -76,25 +83,60 @@ export const assignAgency = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
export const uploadReport = async (req: AuthRequest, res: Response) => {
|
export const uploadReport = async (req: AuthRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { assignmentId, reportDocumentId, findings, recommendation } = req.body;
|
const { assignmentId, reportDocumentId, findings, recommendation, applicationId } = req.body;
|
||||||
|
let finalAssignmentId = assignmentId;
|
||||||
|
|
||||||
const report = await FddReport.create({
|
// Auto-assign logic if assignmentId is missing
|
||||||
assignmentId,
|
if (!finalAssignmentId && applicationId) {
|
||||||
|
const appId = applicationId;
|
||||||
|
const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(appId);
|
||||||
|
const application = await Application.findOne({
|
||||||
|
where: isUUID ? { [Op.or]: [{ id: appId }, { applicationId: appId }] } : { applicationId: appId }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (application) {
|
||||||
|
const [newAssignment] = await FddAssignment.findOrCreate({
|
||||||
|
where: { applicationId: application.id },
|
||||||
|
defaults: {
|
||||||
|
assignedToAgency: req.user?.id,
|
||||||
|
status: 'Assigned'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
finalAssignmentId = newAssignment.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!finalAssignmentId) {
|
||||||
|
return res.status(400).json({ success: false, message: 'Assignment ID or valid Application ID is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
let report = await FddReport.findOne({ where: { assignmentId: finalAssignmentId } });
|
||||||
|
|
||||||
|
if (report) {
|
||||||
|
await report.update({
|
||||||
reportDocumentId,
|
reportDocumentId,
|
||||||
findings,
|
findings,
|
||||||
recommendation,
|
recommendation,
|
||||||
verifiedAt: new Date(),
|
submittedBy: req.user?.id
|
||||||
verifiedBy: req.user?.id // Auto-verified by uploader for now? Or separate verify step?
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
report = await FddReport.create({
|
||||||
|
assignmentId: finalAssignmentId,
|
||||||
|
reportDocumentId,
|
||||||
|
findings,
|
||||||
|
recommendation,
|
||||||
|
submittedBy: req.user?.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Update Assignment Status
|
// Update Assignment Status
|
||||||
await FddAssignment.update(
|
await FddAssignment.update(
|
||||||
{ status: 'Report Submitted' },
|
{ status: 'Report Submitted' },
|
||||||
{ where: { id: assignmentId } }
|
{ where: { id: finalAssignmentId } }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fetch application to transition
|
// Fetch application to transition
|
||||||
const assignment = await FddAssignment.findByPk(assignmentId);
|
const assignment = await FddAssignment.findByPk(finalAssignmentId);
|
||||||
if (assignment) {
|
if (assignment) {
|
||||||
const application = await Application.findByPk(assignment.applicationId);
|
const application = await Application.findByPk(assignment.applicationId);
|
||||||
if (application) {
|
if (application) {
|
||||||
|
|||||||
@ -124,7 +124,7 @@ export const approveRequest = async (req: AuthRequest, res: Response) => {
|
|||||||
const finalDeposit = await SecurityDeposit.findOne({
|
const finalDeposit = await SecurityDeposit.findOne({
|
||||||
where: {
|
where: {
|
||||||
applicationId: request.applicationId,
|
applicationId: request.applicationId,
|
||||||
depositType: 'FINAL',
|
depositType: 'FIRST_FILL',
|
||||||
status: 'Verified'
|
status: 'Verified'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -132,7 +132,7 @@ export const approveRequest = async (req: AuthRequest, res: Response) => {
|
|||||||
if (!finalDeposit) {
|
if (!finalDeposit) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: `LOA Approval Blocked: The Final Security Deposit (₹15L) is either Pending or not found. Finance team must verify the payment before proceeding.`
|
message: `LOA Approval Blocked: The First Fill (₹15L) is either Pending or not found. Finance team must verify the payment before proceeding.`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,7 +312,7 @@ export const updateSecurityDeposit = async (req: AuthRequest, res: Response) =>
|
|||||||
if (!application) return res.status(404).json({ success: false, message: 'Application not found' });
|
if (!application) return res.status(404).json({ success: false, message: 'Application not found' });
|
||||||
|
|
||||||
let deposit = await SecurityDeposit.findOne({
|
let deposit = await SecurityDeposit.findOne({
|
||||||
where: { applicationId: application.id, depositType: depositType || 'INITIAL' }
|
where: { applicationId: application.id, depositType: depositType || 'SECURITY_DEPOSIT' }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (deposit) {
|
if (deposit) {
|
||||||
@ -328,7 +328,7 @@ export const updateSecurityDeposit = async (req: AuthRequest, res: Response) =>
|
|||||||
paymentReference,
|
paymentReference,
|
||||||
proofDocumentId,
|
proofDocumentId,
|
||||||
status: status || 'Pending',
|
status: status || 'Pending',
|
||||||
depositType: depositType || 'INITIAL',
|
depositType: depositType || 'SECURITY_DEPOSIT',
|
||||||
verifiedBy: status === 'Verified' ? req.user?.id : null,
|
verifiedBy: status === 'Verified' ? req.user?.id : null,
|
||||||
verifiedAt: status === 'Verified' ? new Date() : null
|
verifiedAt: status === 'Verified' ? new Date() : null
|
||||||
});
|
});
|
||||||
@ -341,18 +341,18 @@ export const updateSecurityDeposit = async (req: AuthRequest, res: Response) =>
|
|||||||
|
|
||||||
// --- AUTOMATION: After verification transitions ---
|
// --- AUTOMATION: After verification transitions ---
|
||||||
|
|
||||||
// 1. If INITIAL Payment Verified -> Move to LOI Issue Stage
|
// 1. If SECURITY_DEPOSIT Payment Verified -> Move to LOI Issue Stage
|
||||||
if ((depositType === 'INITIAL' || !depositType) && status === 'Verified') {
|
if ((depositType === 'SECURITY_DEPOSIT' || !depositType) && status === 'Verified') {
|
||||||
console.log(`[DEBUG] Initial Security Deposit verified. Moving to LOI Issued stage...`);
|
console.log(`[DEBUG] Security Deposit verified. Moving to LOI Issued stage...`);
|
||||||
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOI_ISSUED, req.user?.id || null, {
|
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOI_ISSUED, req.user?.id || null, {
|
||||||
reason: 'Initial Security Deposit verified. Proceeding to LOI Issuance.',
|
reason: 'Security Deposit verified. Proceeding to LOI Issuance.',
|
||||||
stage: APPLICATION_STAGES.LOI,
|
stage: APPLICATION_STAGES.LOI,
|
||||||
progressPercentage: 80
|
progressPercentage: 80
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. If FINAL Payment Verified -> Move to LOA Pending stage
|
// 2. If FIRST_FILL Payment Verified -> Move to LOA Pending stage
|
||||||
if (depositType === 'FINAL' && status === 'Verified') {
|
if (depositType === 'FIRST_FILL' && status === 'Verified') {
|
||||||
// Ensure LoaRequest exists for the next step
|
// Ensure LoaRequest exists for the next step
|
||||||
await db.LoaRequest.findOrCreate({
|
await db.LoaRequest.findOrCreate({
|
||||||
where: { applicationId: application.id },
|
where: { applicationId: application.id },
|
||||||
@ -360,7 +360,7 @@ export const updateSecurityDeposit = async (req: AuthRequest, res: Response) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOA_PENDING, req.user?.id || null, {
|
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOA_PENDING, req.user?.id || null, {
|
||||||
reason: 'Final Security Deposit Verified. Initiating LOA Approval stage.',
|
reason: 'First Fill Verified. Initiating LOA Approval stage.',
|
||||||
progressPercentage: 90
|
progressPercentage: 90
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -244,7 +244,7 @@ export const approveRequest = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
// Create Initial Security Deposit record (Advance Payment)
|
// Create Initial Security Deposit record (Advance Payment)
|
||||||
await db.SecurityDeposit.findOrCreate({
|
await db.SecurityDeposit.findOrCreate({
|
||||||
where: { applicationId: request.applicationId, depositType: 'INITIAL' },
|
where: { applicationId: request.applicationId, depositType: 'SECURITY_DEPOSIT' },
|
||||||
defaults: {
|
defaults: {
|
||||||
amount: 200000, // 2 Lakhs Advance
|
amount: 200000, // 2 Lakhs Advance
|
||||||
status: 'Pending'
|
status: 'Pending'
|
||||||
|
|||||||
@ -870,7 +870,7 @@ export const generateDealerCodes = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
// Create Final Security Deposit record (Blocker for LOA)
|
// Create Final Security Deposit record (Blocker for LOA)
|
||||||
await db.SecurityDeposit.findOrCreate({
|
await db.SecurityDeposit.findOrCreate({
|
||||||
where: { applicationId: application.id, depositType: 'FINAL' },
|
where: { applicationId: application.id, depositType: 'FIRST_FILL' },
|
||||||
defaults: {
|
defaults: {
|
||||||
amount: 1500000, // 15 Lakhs Final
|
amount: 1500000, // 15 Lakhs Final
|
||||||
status: 'Pending'
|
status: 'Pending'
|
||||||
|
|||||||
@ -100,7 +100,29 @@ export const getRequestById = async (req: AuthRequest, res: Response) => {
|
|||||||
where: isUUID ? { [Op.or]: [{ id: idStr }, { requestId: idStr }] } : { requestId: idStr },
|
where: isUUID ? { [Op.or]: [{ id: idStr }, { requestId: idStr }] } : { requestId: idStr },
|
||||||
include: [
|
include: [
|
||||||
{ model: Outlet, as: 'outlet' },
|
{ model: Outlet, as: 'outlet' },
|
||||||
{ model: User, as: 'dealer', attributes: ['fullName', 'email'] },
|
{
|
||||||
|
model: User,
|
||||||
|
as: 'dealer',
|
||||||
|
attributes: ['id', 'fullName', 'email', 'roleCode'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: db.Dealer,
|
||||||
|
as: 'dealerProfile',
|
||||||
|
include: [
|
||||||
|
{ model: db.DealerCode, as: 'dealerCode' },
|
||||||
|
{
|
||||||
|
model: db.Application,
|
||||||
|
as: 'application',
|
||||||
|
include: [
|
||||||
|
{ model: db.District, as: 'district' },
|
||||||
|
{ model: db.LoiRequest, as: 'loiRequests', where: { status: 'approved' }, required: false },
|
||||||
|
{ model: db.LoaRequest, as: 'loaRequests', where: { status: 'approved' }, required: false }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{ model: Worknote, as: 'worknotes' },
|
{ model: Worknote, as: 'worknotes' },
|
||||||
{
|
{
|
||||||
model: db.RequestParticipant,
|
model: db.RequestParticipant,
|
||||||
|
|||||||
@ -120,7 +120,29 @@ export const getResignationById = async (req: AuthRequest, res: Response, next:
|
|||||||
where: isUUID ? { [Op.or]: [{ id: idStr }, { resignationId: idStr }] } : { resignationId: idStr },
|
where: isUUID ? { [Op.or]: [{ id: idStr }, { resignationId: idStr }] } : { resignationId: idStr },
|
||||||
include: [
|
include: [
|
||||||
{ model: db.Outlet, as: 'outlet' },
|
{ model: db.Outlet, as: 'outlet' },
|
||||||
{ model: db.User, as: 'dealer', attributes: ['id', 'fullName', 'email'] },
|
{
|
||||||
|
model: db.User,
|
||||||
|
as: 'dealer',
|
||||||
|
attributes: ['id', 'fullName', 'email', 'roleCode'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: db.Dealer,
|
||||||
|
as: 'dealerProfile',
|
||||||
|
include: [
|
||||||
|
{ model: db.DealerCode, as: 'dealerCode' },
|
||||||
|
{
|
||||||
|
model: db.Application,
|
||||||
|
as: 'application',
|
||||||
|
include: [
|
||||||
|
{ model: db.District, as: 'district' },
|
||||||
|
{ model: db.LoiRequest, as: 'loiRequests', where: { status: 'approved' }, required: false },
|
||||||
|
{ model: db.LoaRequest, as: 'loaRequests', where: { status: 'approved' }, required: false }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
model: db.ResignationDocument,
|
model: db.ResignationDocument,
|
||||||
as: 'uploadedDocuments',
|
as: 'uploadedDocuments',
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Response, NextFunction } from 'express';
|
|||||||
import db from '../../database/models/index.js';
|
import db from '../../database/models/index.js';
|
||||||
import logger from '../../common/utils/logger.js';
|
import logger from '../../common/utils/logger.js';
|
||||||
import { TERMINATION_STAGES, AUDIT_ACTIONS, ROLES } from '../../common/config/constants.js';
|
import { TERMINATION_STAGES, AUDIT_ACTIONS, ROLES } from '../../common/config/constants.js';
|
||||||
import { Transaction } from 'sequelize';
|
import { Op, Transaction } from 'sequelize';
|
||||||
import { AuthRequest } from '../../types/express.types.js';
|
import { AuthRequest } from '../../types/express.types.js';
|
||||||
import ExternalMocksService from '../../common/utils/externalMocks.service.js';
|
import ExternalMocksService from '../../common/utils/externalMocks.service.js';
|
||||||
|
|
||||||
@ -94,22 +94,38 @@ export const getTerminations = async (req: AuthRequest, res: Response, next: Nex
|
|||||||
export const getTerminationById = async (req: AuthRequest, res: Response, next: NextFunction) => {
|
export const getTerminationById = async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const termination = await db.TerminationRequest.findByPk(id, {
|
const idStr = String(id);
|
||||||
|
const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(idStr);
|
||||||
|
|
||||||
|
const termination = await db.TerminationRequest.findOne({
|
||||||
|
where: isUUID ? { [Op.or]: [{ id: idStr }, { requestId: idStr }] } : { requestId: idStr },
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: db.Dealer,
|
model: db.Dealer,
|
||||||
as: 'dealer',
|
as: 'dealer',
|
||||||
include: [
|
include: [
|
||||||
{ model: db.DealerCode, as: 'dealerCode' },
|
|
||||||
{
|
{
|
||||||
model: db.Application,
|
model: db.Application,
|
||||||
as: 'application',
|
as: 'application',
|
||||||
include: [
|
include: [
|
||||||
{ model: db.District, as: 'district' }
|
{ model: db.District, as: 'district' },
|
||||||
]
|
{
|
||||||
|
model: db.LoiRequest,
|
||||||
|
as: 'loiRequests',
|
||||||
|
where: { status: 'approved' },
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: db.LoaRequest,
|
||||||
|
as: 'loaRequests',
|
||||||
|
where: { status: 'approved' },
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{ model: db.DealerCode, as: 'dealerCode' }
|
||||||
|
]
|
||||||
|
},
|
||||||
{ model: db.User, as: 'initiator', attributes: ['id', 'fullName', 'email'] },
|
{ model: db.User, as: 'initiator', attributes: ['id', 'fullName', 'email'] },
|
||||||
{
|
{
|
||||||
model: db.TerminationDocument,
|
model: db.TerminationDocument,
|
||||||
|
|||||||
@ -81,18 +81,27 @@ export class ParticipantService {
|
|||||||
const termination = await TerminationRequest.findByPk(terminationId);
|
const termination = await TerminationRequest.findByPk(terminationId);
|
||||||
if (!termination) return;
|
if (!termination) return;
|
||||||
|
|
||||||
// TerminationRequest already uses Dealer ID
|
|
||||||
const managers = await this.getDealerLocationManagers(termination.dealerId);
|
|
||||||
const participantIds = new Set<string>();
|
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
|
// 1. Location based managers
|
||||||
|
const managers = await this.getDealerLocationManagers(termination.dealerId);
|
||||||
if (managers) {
|
if (managers) {
|
||||||
if (managers.rbmId) participantIds.add(managers.rbmId);
|
if (managers.rbmId) participantIds.add(managers.rbmId);
|
||||||
if (managers.zbhId) participantIds.add(managers.zbhId);
|
if (managers.zbhId) participantIds.add(managers.zbhId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. National roles
|
// 2. National roles - Crucial for Termination Review
|
||||||
const nationalRoles = [ROLES.DD_LEAD, ROLES.NBH, ROLES.CCO, ROLES.CEO, ROLES.LEGAL_ADMIN];
|
const nationalRoles = [ROLES.DD_LEAD, ROLES.NBH, ROLES.CCO, ROLES.CEO, ROLES.LEGAL_ADMIN, ROLES.SUPER_ADMIN];
|
||||||
const nationalUsers = await User.findAll({
|
const nationalUsers = await User.findAll({
|
||||||
where: {
|
where: {
|
||||||
roleCode: { [Op.in]: nationalRoles },
|
roleCode: { [Op.in]: nationalRoles },
|
||||||
@ -104,11 +113,13 @@ export class ParticipantService {
|
|||||||
nationalUsers.forEach((u: any) => participantIds.add(u.id));
|
nationalUsers.forEach((u: any) => participantIds.add(u.id));
|
||||||
|
|
||||||
// 3. Add all unique participants
|
// 3. Add all unique participants
|
||||||
|
let addedCount = 0;
|
||||||
for (const userId of participantIds) {
|
for (const userId of participantIds) {
|
||||||
await this.addParticipant(termination.id, REQUEST_TYPES.TERMINATION, userId);
|
await this.addParticipant(termination.id, REQUEST_TYPES.TERMINATION, userId);
|
||||||
|
addedCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[ParticipantService] Added ${participantIds.size} participants to termination ${terminationId}`);
|
console.log(`[ParticipantService] Added ${addedCount} participants to termination ${terminationId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error assigning termination participants:', error);
|
console.error('Error assigning termination participants:', error);
|
||||||
}
|
}
|
||||||
@ -122,16 +133,15 @@ export class ParticipantService {
|
|||||||
const request = await ConstitutionalChange.findByPk(requestId);
|
const request = await ConstitutionalChange.findByPk(requestId);
|
||||||
if (!request) return;
|
if (!request) return;
|
||||||
|
|
||||||
// In ConstitutionalChange model, dealerId is the User ID
|
|
||||||
const user = await User.findByPk(request.dealerId);
|
|
||||||
if (!user || !user.dealerId) {
|
|
||||||
console.error(`[ParticipantService] No Dealer ID found for user ${request.dealerId}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const managers = await this.getDealerLocationManagers(user.dealerId);
|
|
||||||
const participantIds = new Set<string>();
|
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
|
// 1. Location based managers
|
||||||
if (managers) {
|
if (managers) {
|
||||||
if (managers.asmId) participantIds.add(managers.asmId);
|
if (managers.asmId) participantIds.add(managers.asmId);
|
||||||
@ -139,9 +149,10 @@ export class ParticipantService {
|
|||||||
if (managers.rbmId) participantIds.add(managers.rbmId);
|
if (managers.rbmId) participantIds.add(managers.rbmId);
|
||||||
if (managers.zbhId) participantIds.add(managers.zbhId);
|
if (managers.zbhId) participantIds.add(managers.zbhId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. National roles
|
// 2. National roles
|
||||||
const nationalRoles = [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.LEGAL_ADMIN];
|
const nationalRoles = [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.LEGAL_ADMIN, ROLES.SUPER_ADMIN];
|
||||||
const nationalUsers = await User.findAll({
|
const nationalUsers = await User.findAll({
|
||||||
where: {
|
where: {
|
||||||
roleCode: { [Op.in]: nationalRoles },
|
roleCode: { [Op.in]: nationalRoles },
|
||||||
@ -153,11 +164,13 @@ export class ParticipantService {
|
|||||||
nationalUsers.forEach((u: any) => participantIds.add(u.id));
|
nationalUsers.forEach((u: any) => participantIds.add(u.id));
|
||||||
|
|
||||||
// 3. Add all unique participants
|
// 3. Add all unique participants
|
||||||
|
let addedCount = 0;
|
||||||
for (const userId of participantIds) {
|
for (const userId of participantIds) {
|
||||||
await this.addParticipant(request.id, REQUEST_TYPES.CONSTITUTIONAL, userId);
|
await this.addParticipant(request.id, REQUEST_TYPES.CONSTITUTIONAL, userId);
|
||||||
|
addedCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[ParticipantService] Added ${participantIds.size} participants to constitutional change ${requestId}`);
|
console.log(`[ParticipantService] Added ${addedCount} participants to constitutional change ${requestId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error assigning constitutional participants:', error);
|
console.error('Error assigning constitutional participants:', error);
|
||||||
}
|
}
|
||||||
@ -169,27 +182,43 @@ export class ParticipantService {
|
|||||||
static async assignResignationParticipants(requestId: string) {
|
static async assignResignationParticipants(requestId: string) {
|
||||||
try {
|
try {
|
||||||
const resignation = await db.Resignation.findByPk(requestId);
|
const resignation = await db.Resignation.findByPk(requestId);
|
||||||
if (!resignation) return;
|
if (!resignation) {
|
||||||
|
console.error(`[ParticipantService] Resignation not found: ${requestId}`);
|
||||||
// In Resignation model, dealerId is the User ID
|
|
||||||
const user = await User.findByPk(resignation.dealerId);
|
|
||||||
if (!user || !user.dealerId) {
|
|
||||||
console.error(`[ParticipantService] No Dealer ID found for user ${resignation.dealerId}`);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const managers = await this.getDealerLocationManagers(user.dealerId);
|
|
||||||
const participantIds = new Set<string>();
|
const participantIds = new Set<string>();
|
||||||
|
|
||||||
// 1. Location based managers
|
// 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) {
|
||||||
if (managers.asmId) participantIds.add(managers.asmId);
|
if (managers.asmId) participantIds.add(managers.asmId);
|
||||||
if (managers.rbmId) participantIds.add(managers.rbmId);
|
if (managers.rbmId) participantIds.add(managers.rbmId);
|
||||||
if (managers.zbhId) participantIds.add(managers.zbhId);
|
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
|
||||||
|
];
|
||||||
|
|
||||||
// 2. National roles
|
|
||||||
const nationalRoles = [ROLES.DD_LEAD, ROLES.NBH, ROLES.DD_ADMIN, ROLES.FINANCE, ROLES.LEGAL_ADMIN];
|
|
||||||
const nationalUsers = await User.findAll({
|
const nationalUsers = await User.findAll({
|
||||||
where: {
|
where: {
|
||||||
roleCode: { [Op.in]: nationalRoles },
|
roleCode: { [Op.in]: nationalRoles },
|
||||||
@ -201,11 +230,15 @@ export class ParticipantService {
|
|||||||
nationalUsers.forEach((u: any) => participantIds.add(u.id));
|
nationalUsers.forEach((u: any) => participantIds.add(u.id));
|
||||||
|
|
||||||
// 3. Add all unique participants
|
// 3. Add all unique participants
|
||||||
|
let addedCount = 0;
|
||||||
for (const userId of participantIds) {
|
for (const userId of participantIds) {
|
||||||
await this.addParticipant(resignation.id, REQUEST_TYPES.RESIGNATION, userId);
|
// 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 ${participantIds.size} participants to resignation ${requestId}`);
|
console.log(`[ParticipantService] Added ${addedCount} participants to resignation ${requestId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error assigning resignation participants:', error);
|
console.error('Error assigning resignation participants:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
30
sync_participants.ts
Normal file
30
sync_participants.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import db from './src/database/models/index.js';
|
||||||
|
import { ParticipantService } from './src/services/ParticipantService';
|
||||||
|
|
||||||
|
async function syncAll() {
|
||||||
|
try {
|
||||||
|
const terminations = await db.TerminationRequest.findAll();
|
||||||
|
console.log(`Found ${terminations.length} terminations to sync...`);
|
||||||
|
|
||||||
|
for (const term of terminations) {
|
||||||
|
console.log(`Mapping participants for ${term.requestId} (${term.id})...`);
|
||||||
|
await ParticipantService.assignTerminationParticipants(term.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const changes = await db.ConstitutionalChange.findAll();
|
||||||
|
console.log(`Found ${changes.length} constitutional changes to sync...`);
|
||||||
|
for (const change of changes) {
|
||||||
|
console.log(`Mapping participants for ${change.requestId} (${change.id})...`);
|
||||||
|
await ParticipantService.assignConstitutionalParticipants(change.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Sync completed.');
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Sync failed:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncAll();
|
||||||
@ -31,7 +31,12 @@ async function apiRequest(endpoint, method = 'GET', body = null, token = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function login(email) {
|
async function login(email) {
|
||||||
const data = await apiRequest('/auth/login', 'POST', { email, password: (email === 'dealer@royalenfield.com' ? 'Admin@123' : PASSWORD) });
|
const isInternal = email.endsWith('@royalenfield.com') ||
|
||||||
|
email === 'lince@gmail.com' ||
|
||||||
|
email === 'yashwin@gmail.com';
|
||||||
|
const password = isInternal ? 'Admin@123' : 'Dealer@123';
|
||||||
|
|
||||||
|
const data = await apiRequest('/auth/login', 'POST', { email, password });
|
||||||
return data.token;
|
return data.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -94,7 +94,7 @@ async function prospectLogin(phone) {
|
|||||||
|
|
||||||
async function mockUploadDocument(appId, token, docType) {
|
async function mockUploadDocument(appId, token, docType) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
const fileBuffer = fs.readFileSync('C:/Users/BACKPACKERS/Pictures/claim_document_type.PNG');
|
const fileBuffer = fs.readFileSync('/home/laxman-h/Pictures/Screenshots/Screenshot from 2026-03-26 10-08-00.png');
|
||||||
const blob = new Blob([fileBuffer], { type: 'image/png' });
|
const blob = new Blob([fileBuffer], { type: 'image/png' });
|
||||||
formData.append('file', blob, 'screenshot.png');
|
formData.append('file', blob, 'screenshot.png');
|
||||||
formData.append('documentType', docType);
|
formData.append('documentType', docType);
|
||||||
@ -364,17 +364,17 @@ async function triggerWorkflow() {
|
|||||||
applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
amount: 500000,
|
amount: 500000,
|
||||||
paymentReference: 'PAY-888999',
|
paymentReference: 'PAY-888999',
|
||||||
depositType: 'INITIAL',
|
depositType: 'SECURITY_DEPOSIT',
|
||||||
status: 'Verified'
|
status: 'Verified'
|
||||||
}, financeToken);
|
}, financeToken);
|
||||||
log(9, 'Initial Security Deposit Verified.');
|
log(9, 'Security Deposit Verified.');
|
||||||
|
|
||||||
log(9.1, 'Finance Verifying FINAL Security Deposit (₹15L)...');
|
log(9.1, 'Finance Verifying FIRST FILL (₹15L)...');
|
||||||
await apiRequest('/loa/security-deposit', 'POST', {
|
await apiRequest('/loa/security-deposit', 'POST', {
|
||||||
applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
amount: 1500000,
|
amount: 1500000,
|
||||||
paymentReference: 'PAY-FIN-999',
|
paymentReference: 'PAY-FIN-999',
|
||||||
depositType: 'FINAL',
|
depositType: 'FIRST_FILL',
|
||||||
status: 'Verified'
|
status: 'Verified'
|
||||||
}, financeToken);
|
}, financeToken);
|
||||||
log(9.1, 'Final Security Deposit Verified.');
|
log(9.1, 'Final Security Deposit Verified.');
|
||||||
@ -383,14 +383,14 @@ async function triggerWorkflow() {
|
|||||||
// 10. FINAL LOA APPROVAL
|
// 10. FINAL LOA APPROVAL
|
||||||
log(10, 'NBH & Head Approving Final LOA...');
|
log(10, 'NBH & Head Approving Final LOA...');
|
||||||
const loaRes = await apiRequest('/loa/request', 'POST', { applicationId: applicationUUID }, headToken);
|
const loaRes = await apiRequest('/loa/request', 'POST', { applicationId: applicationUUID }, headToken);
|
||||||
loaRequestId = loaRes.data.id;
|
const finalLoaRequestId = loaRes.data.id;
|
||||||
|
|
||||||
await apiRequest(`/loa/request/${loaRequestId}/approve`, 'POST', {
|
await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', {
|
||||||
action: 'Approved',
|
action: 'Approved',
|
||||||
remarks: 'Head Authorization (Level 1)'
|
remarks: 'Head Authorization (Level 1)'
|
||||||
}, headToken);
|
}, headToken);
|
||||||
|
|
||||||
await apiRequest(`/loa/request/${loaRequestId}/approve`, 'POST', {
|
await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', {
|
||||||
action: 'Approved',
|
action: 'Approved',
|
||||||
remarks: 'NBH Approval (Level 2)'
|
remarks: 'NBH Approval (Level 2)'
|
||||||
}, nbhToken);
|
}, nbhToken);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user