Dealer_Onboarding_Backend/trigger-workflow.js

426 lines
15 KiB
JavaScript

/**
* ROYAL ENFIELD DEALER ONBOARDING - END-TO-END WORKFLOW TRIGGER
* This script automates the entire journey from Application to LOA.
*/
import fs from 'fs';
const BASE_URL = 'http://localhost:5000/api';
const PASSWORD = 'Admin@123';
const OTP = '123456';
// Append timestamp to email to avoid duplicate application error
const timestamp = Date.now();
const PROSPECT_EMAIL = `ramesh_${timestamp}@gmail.com`;
const EMAILS = {
PROSPECT: PROSPECT_EMAIL,
RBM_L1: 'rbm.ncr@royalenfield.com',
ZM_L1: 'zm.ncr@royalenfield.com',
DD_LEAD: 'ddlead@royalenfield.com',
ZBH: 'yashwin@gmail.com',
NBH: 'nbh@royalenfield.com',
DD_HEAD: 'ddhead@royalenfield.com',
FDD: 'fdd@royalenfield.com',
FINANCE: 'finance@royalenfield.com',
DD_ADMIN: 'lince@gmail.com',
ASM: 'asm.sdelhi@royalenfield.com'
};
const PROSPECT_PAYLOAD = {
applicantName: "ramesh",
email: PROSPECT_EMAIL,
phone: "8197735918",
businessType: "Dealership",
locationType: "Urban",
district: "South Delhi",
city: "South Delhi",
state: "DELHI",
preferredLocation: "Indiranagar",
address: "123, 100ft Road",
pincode: "520038",
experienceYears: 5,
investmentCapacity: "2-3 Cr",
age: 32,
education: "MBA",
companyName: "Kumar Automobiles",
source: "Website",
existingDealer: "No",
ownRoyalEnfield: "Yes",
royalEnfieldModel: "Classic 350",
description: "Interested in opening a main dealership."
};
// State to store IDs between steps
let applicationId = null;
let applicationUUID = null;
let interviewId = null;
let loaRequestId = null;
/**
* HELPERS
*/
const delay = (ms = 5000) => new Promise(res => setTimeout(res, ms));
const log = (step, msg) => console.log(`[STEP ${step}] ${msg}`);
async function apiRequest(endpoint, method = 'GET', body = null, token = null) {
const headers = { 'Content-Type': 'application/json' };
if (token) headers['Authorization'] = `Bearer ${token}`;
const config = { method, headers };
if (body) config.body = JSON.stringify(body);
const response = await fetch(`${BASE_URL}${endpoint}`, config);
const data = await response.json();
if (!response.ok) {
console.error(`\x1b[31mFAIL: ${method} ${endpoint} returned ${response.status}\x1b[0m`);
console.error(JSON.stringify(data, null, 2));
process.exit(1);
}
return data;
}
async function login(email) {
const data = await apiRequest('/auth/login', 'POST', { email, password: PASSWORD });
return data.token; // Standard login returns token at root
}
async function prospectLogin(phone) {
await apiRequest('/prospective-login/send-otp', 'POST', { phone });
const data = await apiRequest('/prospective-login/verify-otp', 'POST', { phone, otp: OTP });
return data.data.token; // Prospect OTP returns token inside data object
}
async function mockUploadDocument(appId, token, docType) {
const formData = new FormData();
const fileBuffer = fs.readFileSync('/home/laxman-h/Pictures/Screenshots/Screenshot from 2026-03-27 09-48-22.png');
const blob = new Blob([fileBuffer], { type: 'image/png' });
formData.append('file', blob, 'screenshot.png');
formData.append('documentType', docType);
const headers = { 'Authorization': `Bearer ${token}` };
const response = await fetch(`${BASE_URL}/onboarding/applications/${appId}/documents`, {
method: 'POST',
headers,
body: formData
});
if (!response.ok) {
throw new Error(`Upload Failed: ${response.status}`);
}
return response.json();
}
/**
* MAIN WORKFLOW
*/
async function triggerWorkflow() {
console.log('--- STARTING DEALER ONBOARDING E2E FLOW ---\n');
// 1. PUBLIC APPLY
log(1, 'Public Prospect Application Submission...');
const appResponse = await apiRequest('/onboarding/apply', 'POST', PROSPECT_PAYLOAD);
applicationId = appResponse.data.applicationId;
applicationUUID = appResponse.data.id;
log(1, `Application Created: ${applicationId} (UUID: ${applicationUUID})`);
await delay();
// 2. ADMIN SHORTLIST
log(2, 'Admin Login & Shortlisting...');
const adminToken = await login(EMAILS.DD_ADMIN);
// Find the ASM to assign (just picking the first user with role DD-ASM if we could,
// but for now assigning to DD-Lead for testing)
const users = await apiRequest('/admin/users', 'GET', null, adminToken);
const ddLead = users.data.find(u => u.email === EMAILS.ASM);
await apiRequest('/onboarding/applications/shortlist', 'POST', {
applicationIds: [applicationId],
assignedTo: [ddLead.id],
remarks: 'Shortlisted for evaluation'
}, adminToken);
log(2, 'Application Shortlisted successfully.');
await delay();
// 3. SKIP QUESTIONNAIRE FOR TESTING STABILITY
log(3, 'Skipping Questionnaire stage for test stability...');
await delay();
// 4. LEVEL-1 INTERVIEW
log(4, 'ASM Scheduling Level 1 Interview...');
const leadToken = await login(EMAILS.DD_LEAD);
const rbmUser = users.data.find(u => u.email === EMAILS.RBM_L1);
const zmUser = users.data.find(u => u.email === EMAILS.ZM_L1);
const zbhUser = users.data.find(u => u.email === EMAILS.ZBH) || users.data[2];
const intvResponse = await apiRequest('/assessment/interviews', 'POST', {
applicationId: applicationUUID,
level: 1,
scheduledAt: new Date(Date.now() + 86400000).toISOString(),
type: 'In-Person',
location: 'Zonal Office',
participants: [rbmUser.id, zmUser.id]
}, leadToken);
interviewId = intvResponse.data.id;
log(4, `Level 1 Interview Scheduled (ID: ${interviewId})`);
await delay();
// FEEDBACK RBM
log(4.1, 'RBM Giving Feedback...');
const rbmToken = await login(EMAILS.RBM_L1);
await apiRequest('/assessment/kt-matrix', 'POST', {
interviewId,
criteriaScores: [{ criterionName: 'Business Acumen', score: 8.5, maxScore: 10, weightage: 100 }],
feedback: 'Strong business acumen.',
recommendation: 'Selected'
}, rbmToken);
// FEEDBACK ZM
log(4.2, 'ZM Giving Feedback...');
const zmToken = await login(EMAILS.ZM_L1);
await apiRequest('/assessment/kt-matrix', 'POST', {
interviewId,
criteriaScores: [{ criterionName: 'Vision', score: 9.0, maxScore: 10, weightage: 100 }],
feedback: 'Good vision for RE brand.',
recommendation: 'Selected'
}, zmToken);
// ZM DECISION (Rajesh Khanna)
log(4.3, 'ZM Finalizing Level 1 Decision...');
await apiRequest('/assessment/decision', 'POST', {
interviewId,
decision: 'Approved',
remarks: 'Cleared Level 1'
}, zmToken);
log(4, 'Level 1 Complete.');
await delay();
// 5. LEVEL-2 INTERVIEW
log(5, 'Scheduling Level 2 Interview...');
const intv2Response = await apiRequest('/assessment/interviews', 'POST', {
applicationId: applicationUUID,
level: 2,
scheduledAt: new Date(Date.now() + 172800000).toISOString(),
type: 'Online',
location: 'Teams',
participants: [ddLead.id, zbhUser.id]
}, leadToken);
const interviewId2 = intv2Response.data.id;
log(5.1, 'DD-Lead Giving Feedback...');
await apiRequest('/assessment/level2-feedback', 'POST', {
interviewId: interviewId2,
overallScore: 9.5,
feedbackItems: [
{ type: 'Strategic Vision', comments: 'Excellent strategic planning.' },
{ type: 'Management Capabilities', comments: 'Strong team leadership.' },
{ type: 'Operational Understanding', comments: 'Knows the local market well.' }
],
recommendation: 'Selected'
}, leadToken);
log(5.15, 'ZBH Giving Feedback...');
const zbhToken = await login(zbhUser.email);
await apiRequest('/assessment/level2-feedback', 'POST', {
interviewId: interviewId2,
overallScore: 9.0,
feedbackItems: [
{ type: 'Strategic Vision', comments: 'Good alignment with brand.' },
{ type: 'Key Strengths', comments: 'Great location proposed.' },
{ type: 'Areas of Concern', comments: 'None at this time.' }
],
recommendation: 'Selected'
}, zbhToken);
log(5.2, 'DD-Lead Finalizing Level 2 Decision...');
await apiRequest('/assessment/decision', 'POST', {
interviewId: interviewId2,
decision: 'Approved',
remarks: 'Cleared Level 2'
}, leadToken);
log(5, 'Level 2 Complete.');
await delay();
// 6. LEVEL-3 INTERVIEW
log(6, 'Scheduling Level 3 Interview...');
const headUser = users.data.find(u => u.email === EMAILS.DD_HEAD);
const nbhUser = users.data.find(u => u.email === EMAILS.NBH);
const intv3Response = await apiRequest('/assessment/interviews', 'POST', {
applicationId: applicationUUID,
level: 3,
scheduledAt: new Date(Date.now() + 259200000).toISOString(),
type: 'In-Person',
location: 'HO',
participants: [headUser.id, nbhUser.id]
}, leadToken);
const interviewId3 = intv3Response.data.id;
log(6.1, 'NBH Giving Feedback...');
const nbhToken = await login(EMAILS.NBH);
await apiRequest('/assessment/level2-feedback', 'POST', {
interviewId: interviewId3,
overallScore: 10,
feedbackItems: [
{ type: 'Business Vision & Strategy', comments: 'Highly recommended for this market.' },
{ type: 'Leadership & Decision Making', comments: 'Shows great potential.' }
],
recommendation: 'Selected'
}, nbhToken);
log(6.15, 'DD-Head Giving Feedback...');
const headToken = await login(EMAILS.DD_HEAD);
await apiRequest('/assessment/level2-feedback', 'POST', {
interviewId: interviewId3,
overallScore: 9.5,
feedbackItems: [
{ type: 'Operational & Financial Readiness', comments: 'Financially sound.' },
{ type: 'Brand Alignment', comments: 'Understands Royal Enfield ethos perfectly.' }
],
recommendation: 'Selected'
}, headToken);
log(6.2, 'Head Finalizing Level 3 Decision...');
await apiRequest('/assessment/decision', 'POST', {
interviewId: interviewId3,
decision: 'Approved',
remarks: 'Cleared Level 3. Moving to FDD.'
}, headToken);
log(6, 'Level 3 Complete. Stage is now FDD Verification.');
await delay();
// 6.3 FDD ASSIGNMENT
log(6.3, 'Admin Assigning Application to FDD Agency...');
const fddUser = users.data.find(u => u.email === EMAILS.FDD);
await apiRequest('/fdd/assign', 'POST', {
applicationId: applicationUUID,
assignedToAgency: fddUser.id
}, adminToken);
log(6.3, 'FDD Agency assigned successfully.');
await delay();
// 7. FDD MILESTONE
log(7, 'FDD Agency Discovery & Report Upload...');
const fddToken = await login(EMAILS.FDD);
// FETCH ASSIGNMENT ID
const assignmentRes = await apiRequest(`/fdd/${applicationUUID}`, 'GET', null, fddToken);
const assignmentId = assignmentRes.data.id;
log(7, `Found Assignment ID: ${assignmentId}`);
await apiRequest('/fdd/report', 'POST', {
assignmentId,
findings: 'Finance records clean.',
recommendation: 'Approved'
}, fddToken);
log(7.1, 'Admin Approving FDD Final Stage...');
await apiRequest('/assessment/stage-decision', 'POST', {
applicationId: applicationUUID,
stageCode: 'FDD_VERIFICATION',
decision: 'Approved',
remarks: 'FDD documents verified.'
}, adminToken);
log(7, 'FDD Milestone Complete.');
await delay();
log(7.4, 'Uploading mandatory documents prior to LOI generation...');
const requiredDocs = ['CIBIL Report', 'Proposed Site City Map', 'Bank Statement', 'GST Certificate', 'PAN Card'];
for (const doc of requiredDocs) {
await mockUploadDocument(applicationUUID, adminToken, doc);
}
await delay(1000);
// 7.5 LOI APPROVAL
log(7.5, 'LOI Generation & Approval...');
const loiRes = await apiRequest('/loi/request', 'POST', { applicationId: applicationUUID }, adminToken);
const loiRequestId = loiRes.data.id;
// Head Approval
await apiRequest(`/loi/request/${loiRequestId}/approve`, 'POST', {
action: 'Approved',
remarks: 'Head Authorization for LOI'
}, headToken);
// NBH Approval
await apiRequest(`/loi/request/${loiRequestId}/approve`, 'POST', {
action: 'Approved',
remarks: 'NBH Authorization for LOI'
}, nbhToken);
log(7.5, 'LOI Milestone Complete.');
await delay();
// 8. GENERATE DEALER CODES (Sequence: Post-LOI, Pre-LOA)
log(8, 'Admin Generating SAP Dealer Codes...');
await apiRequest(`/onboarding/applications/${applicationUUID}/generate-codes`, 'POST', {}, adminToken);
log(8, 'Dealer Codes Generated.');
await delay();
// 9. PAYMENT GATE
log(9, 'Prospect Uploading Payment Receipt (Mock)...');
const financeToken = await login(EMAILS.FINANCE);
await apiRequest('/loa/security-deposit', 'POST', {
applicationId: applicationUUID,
amount: 500000,
paymentReference: 'PAY-888999',
depositType: 'INITIAL',
status: 'Verified'
}, financeToken);
log(9, 'Initial Security Deposit Verified.');
log(9.1, 'Finance Verifying FINAL Security Deposit (₹15L)...');
await apiRequest('/loa/security-deposit', 'POST', {
applicationId: applicationUUID,
amount: 1500000,
paymentReference: 'PAY-FIN-999',
depositType: 'FINAL',
status: 'Verified'
}, financeToken);
log(9.1, 'Final Security Deposit Verified.');
await delay();
// 10. FINAL LOA APPROVAL
log(10, 'NBH & Head Approving Final LOA...');
const loaRes = await apiRequest('/loa/request', 'POST', { applicationId: applicationUUID }, headToken);
loaRequestId = loaRes.data.id;
await apiRequest(`/loa/request/${loaRequestId}/approve`, 'POST', {
action: 'Approved',
remarks: 'Head Authorization (Level 1)'
}, headToken);
await apiRequest(`/loa/request/${loaRequestId}/approve`, 'POST', {
action: 'Approved',
remarks: 'NBH Approval (Level 2)'
}, nbhToken);
log(10, 'LOA Fully Approved.');
await delay();
// 11. MOVE TO INAUGURATION / APPROVED (Manual Transition)
log(11, 'Admin Moving Application to Approved stage for final onboarding...');
await apiRequest(`/onboarding/applications/${applicationUUID}/status`, 'PUT', {
status: 'Approved',
stage: 'Inauguration',
reason: 'Pre-onboarding verification complete'
}, adminToken);
log(11, 'Application is now in Approved status.');
await delay();
// 12. FINAL ONBOARDING
log(12, 'Admin Finalizing Dealer Onboarding...');
await apiRequest('/dealers', 'POST', { applicationId: applicationUUID }, adminToken);
log(12, '--- WORKFLOW COMPLETED SUCCESSFULLY! ---');
log(12, `The application ${applicationId} is now at 'ONBOARDED' status.`);
}
/**
* START
*/
triggerWorkflow().catch(err => {
console.error('\x1b[31mCRITICAL FAILURE during workflow execution:\x1b[0m');
console.error(err);
process.exit(1);
});