/** * 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', SALES: 'sales@royalenfield.com', SERVICE: 'service@royalenfield.com', SPARES: 'spares@royalenfield.com', ACCOUNTS: 'accounts@royalenfield.com', WARRANTY: 'warranty@royalenfield.com', MARKETING: 'marketing@royalenfield.com', HR: 'hr@royalenfield.com', IT: 'it@royalenfield.com', LEGAL: 'legal@royalenfield.com', LOGISTICS: 'logistics@royalenfield.com', QUALITY: 'quality@royalenfield.com', APPAREL: 'apparel@royalenfield.com', DMS: 'dms@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.", panNumber: 'ABCDE1234F', gstNumber: '07ABCDE1234F1Z5', bankName: 'HDFC Bank', accountNumber: '50100223344556', ifscCode: 'HDFC0001234', accountHolderName: 'Kumar Automobiles Private Limited', registeredAddress: '123, Main Road, New Delhi' }; // 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-26 10-08-00.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: 'SECURITY_DEPOSIT', status: 'Verified' }, financeToken); log(9, 'Security Deposit Verified.'); log(9.1, 'Finance Verifying FIRST FILL (₹15L)...'); await apiRequest('/loa/security-deposit', 'POST', { applicationId: applicationUUID, amount: 1500000, paymentReference: 'PAY-FIN-999', depositType: 'FIRST_FILL', status: 'Verified' }, financeToken); log(9.1, 'Final Security Deposit Verified.'); await delay(); // 9.2 ADMIN UPDATING STATUTORY & BANK DETAILS log(9.2, 'Admin Updating Statutory & Bank Details for LOA Approval Gate...'); await apiRequest(`/onboarding/applications/${applicationUUID}`, 'PUT', { accountHolderName: 'Ramesh Automobiles Private Limited', panNumber: 'ABCDE1234F', gstNumber: '07ABCDE1234F1Z5', bankName: 'HDFC Bank', accountNumber: '50100223344556', ifscCode: 'HDFC0001234' }, adminToken); log(9.2, 'Statutory & Bank details updated.'); await delay(); // 10. FINAL LOA APPROVAL log(10, 'NBH & Head Approving Final LOA...'); const loaRes = await apiRequest('/loa/request', 'POST', { applicationId: applicationUUID }, headToken); const finalLoaRequestId = loaRes.data.id; await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', { action: 'Approved', remarks: 'Head Authorization (Level 1)' }, headToken); await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', { action: 'Approved', remarks: 'NBH Approval (Level 2)' }, nbhToken); log(10, 'LOA Fully Approved.'); await delay(); // 11. EOR (EVIDENCE OF READINESS) CHECKLIST VERIFICATION log(11, 'Admin Initializing EOR Checklist (100% Readiness Requirement)...'); const eorInit = await apiRequest('/eor', 'POST', { applicationId: applicationUUID }, adminToken); const checklistId = eorInit.data.id; log(11, `EOR Checklist Created (ID: ${checklistId})`); log(11.1, 'Auditor Verifying all 12 mandatory EOR items as COMPLIANT...'); const eorItems = [ { itemType: 'Sales', description: 'Sales Standards' }, { itemType: 'Service', description: 'Service & Spares' }, { itemType: 'IT', description: 'DMS infra' }, { itemType: 'Training', description: 'Manpower Training' }, { itemType: 'Statutory', description: 'Trade certificate with test ride bikes registration' }, { itemType: 'Statutory', description: 'GST certificate including Accessories & Apparels billing' }, { itemType: 'Finance', description: 'Inventory Funding' }, { itemType: 'IT', description: 'Virtual code availability' }, { itemType: 'Finance', description: 'Vendor payments' }, { itemType: 'Marketing', description: 'Details for website submission' }, { itemType: 'Insurance', description: 'Infra Insurance both Showroom and Service center' }, { itemType: 'IT', description: 'Auto ordering' } ]; for (const item of eorItems) { process.stdout.write(`.`); // Visual progress await apiRequest(`/eor/item/${checklistId}`, 'POST', { ...item, isCompliant: true, remarks: 'Verified by Auditor - Compliant' }, adminToken); } console.log('\n[STEP 11.1] All EOR items marked as compliant.'); log(11.2, 'Auditor Submitting Final EOR Audit...'); await apiRequest(`/eor/audit/${checklistId}`, 'POST', { status: 'Completed', overallComments: 'Dealer is 100% ready for inauguration. All infra and statutory items verified.' }, adminToken); // Status check const finalAppStatus = await apiRequest(`/onboarding/applications/${applicationUUID}`, 'GET', null, adminToken); log(11.2, `Application Status after EOR: ${finalAppStatus.data.overallStatus}`); await delay(); // 12. FINAL ONBOARDING log(12, 'Admin Finalizing Dealer Onboarding...'); await apiRequest('/dealers', 'POST', { applicationId: applicationUUID }, adminToken); await delay(); // 13. VERIFICATION log(13, 'Verifying Dealer Record Creation...'); const dealerRes = await apiRequest(`/dealers/application/${applicationUUID}`, 'GET', null, adminToken); if (!dealerRes.success || !dealerRes.data) { throw new Error('Verification Failed: Dealer record not found after onboarding.'); } log(13, `Dealer Found: ${dealerRes.data.legalName} (${dealerRes.data.id})`); log(13.1, 'Verifying User Account Role Update...'); const userRes = await apiRequest(`/admin/users`, 'GET', null, adminToken); const dealerUser = userRes.data.find(u => u.email === PROSPECT_EMAIL); if (!dealerUser || dealerUser.roleCode !== 'Dealer') { throw new Error(`Verification Failed: User role not updated to 'Dealer'. Current role: ${dealerUser?.roleCode}`); } log(13.1, `User role confirmed: ${dealerUser.roleCode}`); log(13.2, '--- WORKFLOW COMPLETED SUCCESSFULLY! ---'); log(13.2, `The application ${applicationId} is now at 'ONBOARDED' status and Dealer profile is active.`); } /** * START */ triggerWorkflow().catch(err => { console.error('\x1b[31mCRITICAL FAILURE during workflow execution:\x1b[0m'); console.error(err); process.exit(1); });