29 KiB
Royal Enfield Dealership Onboarding System - Backend Documentation
📋 Overview
This is a comprehensive Node.js + Express backend for the Royal Enfield Dealership Onboarding System. It handles a multi-stage workflow with 13 different user roles, managing dealer applications, resignations, constitutional changes, relocations, and full F&F settlement processes.
🏗️ Architecture
Tech Stack:
- Runtime: Node.js (v18+)
- Framework: Express.js
- Database: PostgreSQL (with Sequelize ORM)
- Authentication: JWT (JSON Web Tokens)
- File Storage: Multer + Local/Cloud storage
- Email: Nodemailer
- Validation: express-validator
- Security: bcryptjs, helmet, cors
📁 Project Structure
backend/
├── back.md # This comprehensive documentation file
├── package.json # Dependencies and scripts
├── .env.example # Environment variables template
├── server.js # Main entry point
├── config/
│ ├── database.js # Database configuration
│ ├── email.js # Email service configuration
│ └── constants.js # System constants (roles, statuses, stages)
├── models/
│ ├── index.js # Sequelize model loader
│ ├── User.js # User model
│ ├── Application.js # Dealer application model
│ ├── Resignation.js # Resignation request model
│ ├── ConstitutionalChange.js # Constitutional change model
│ ├── RelocationRequest.js # Relocation request model
│ ├── Outlet.js # Dealership/Studio outlet model
│ ├── Worknote.js # Discussion worknotes model
│ ├── Document.js # Document uploads model
│ ├── AuditLog.js # Audit trail model
│ ├── FinancePayment.js # Finance payment tracking
│ ├── FnF.js # Full & Final settlement
│ ├── Region.js # Regional hierarchy model
│ └── Zone.js # Zone model
├── controllers/
│ ├── authController.js # Login, logout, token refresh
│ ├── userController.js # User management
│ ├── applicationController.js # Dealer applications
│ ├── resignationController.js # Resignation workflow
│ ├── constitutionalController.js # Constitutional changes
│ ├── relocationController.js # Relocation requests
│ ├── outletController.js # Outlet management
│ ├── worknoteController.js # Discussion platform
│ ├── financeController.js # Finance operations
│ ├── masterController.js # Master configuration
│ └── dashboardController.js # Dashboard statistics
├── routes/
│ ├── auth.js # Authentication routes
│ ├── users.js # User routes
│ ├── applications.js # Application routes
│ ├── resignations.js # Resignation routes
│ ├── constitutional.js # Constitutional change routes
│ ├── relocations.js # Relocation routes
│ ├── outlets.js # Outlet routes
│ ├── worknotes.js # Worknote routes
│ ├── finance.js # Finance routes
│ ├── master.js # Master configuration routes
│ └── dashboard.js # Dashboard routes
├── middleware/
│ ├── auth.js # JWT verification middleware
│ ├── roleCheck.js # Role-based access control
│ ├── upload.js # File upload middleware
│ ├── validation.js # Input validation
│ └── errorHandler.js # Global error handler
├── utils/
│ ├── emailTemplates.js # Email HTML templates
│ ├── emailService.js # Email sending utilities
│ ├── logger.js # Winston logger
│ └── helpers.js # Helper functions
└── migrations/ # Database migrations
└── initial-setup.js # Initial database schema
👥 User Roles & Permissions
The system supports 13 distinct roles with hierarchical permissions:
- DD (Dealer Development) - Creates applications, initial review
- DD-ZM (DD Zone Manager) - Zone-level approvals
- RBM (Regional Business Manager) - Regional approvals
- ZBH (Zonal Business Head) - Zonal head approvals
- DD Lead - Organization-wide DD leadership (singular position)
- DD Head - Head of DD department (singular position)
- NBH (National Business Head) - National head (singular position)
- DD Admin - Administrative operations
- Legal Admin - Legal clearance and document verification
- Super Admin - Full system access
- DD AM (Area Manager) - Area-level management
- Finance - Payment tracking and financial approvals
- Dealer - Dealer portal access (resignation, relocation, constitutional changes)
🗄️ Database Schema
Key Tables:
1. users
- id (UUID, PK)
- email (STRING, UNIQUE)
- password (STRING, hashed)
- role (ENUM: DD, DD-ZM, RBM, ZBH, DD Lead, DD Head, NBH, DD Admin, Legal Admin, Super Admin, DD AM, Finance, Dealer)
- name (STRING)
- region (STRING) - East, West, North, South, Central
- zone (STRING)
- status (ENUM: active, inactive)
- createdAt, updatedAt
2. applications
- id (UUID, PK)
- applicationId (STRING, UNIQUE) - e.g., APP-2026-001
- applicantName (STRING)
- email (STRING)
- phone (STRING)
- businessType (ENUM: Dealership, Studio)
- proposedLocation (TEXT)
- city, state, pincode
- currentStage (ENUM: DD, DD-ZM, RBM, ZBH, DD Lead, DD Head, NBH, Legal, Finance, Approved, Rejected)
- status (ENUM: Pending, In Review, Approved, Rejected)
- ranking (INTEGER) - DD ranking system
- submittedBy (UUID, FK -> users)
- submittedAt (DATE)
- progressPercentage (INTEGER)
- documents (JSON) - Array of document references
- createdAt, updatedAt
3. outlets
- id (UUID, PK)
- code (STRING, UNIQUE) - e.g., DL-MH-001, ST-MH-002
- name (STRING)
- type (ENUM: Dealership, Studio)
- address (TEXT)
- city (STRING)
- state (STRING)
- pincode (STRING)
- latitude (DECIMAL)
- longitude (DECIMAL)
- status (ENUM: Active, Pending Resignation, Closed)
- establishedDate (DATE)
- dealerId (UUID, FK -> users)
- region (STRING)
- zone (STRING)
- createdAt, updatedAt
4. resignations
- id (UUID, PK)
- resignationId (STRING, UNIQUE) - e.g., RES-001
- outletId (UUID, FK -> outlets)
- dealerId (UUID, FK -> users)
- resignationType (ENUM: Voluntary, Retirement, Health Issues, Business Closure, Other)
- lastOperationalDateSales (DATE)
- lastOperationalDateServices (DATE)
- reason (TEXT)
- additionalInfo (TEXT)
- currentStage (ENUM: ASM, RBM, ZBH, NBH, DD Admin, Legal, Finance, Completed, Rejected)
- status (STRING)
- progressPercentage (INTEGER)
- submittedOn (DATE)
- documents (JSON)
- createdAt, updatedAt
5. constitutional_changes
- id (UUID, PK)
- requestId (STRING, UNIQUE) - e.g., CC-001
- outletId (UUID, FK -> outlets)
- dealerId (UUID, FK -> users)
- changeType (ENUM: Ownership Transfer, Partnership Change, LLP Conversion, Company Formation, Director Change)
- currentStructure (STRING)
- proposedStructure (STRING)
- reason (TEXT)
- effectiveDate (DATE)
- currentStage (ENUM: DD Admin Review, Legal Review, NBH Approval, Finance Clearance, Completed, Rejected)
- status (STRING)
- progressPercentage (INTEGER)
- submittedOn (DATE)
- documents (JSON)
- createdAt, updatedAt
6. relocation_requests
- id (UUID, PK)
- requestId (STRING, UNIQUE) - e.g., REL-001
- outletId (UUID, FK -> outlets)
- dealerId (UUID, FK -> users)
- relocationType (ENUM: Within City, Intercity, Interstate)
- currentAddress (TEXT)
- currentLatitude (DECIMAL)
- currentLongitude (DECIMAL)
- proposedAddress (TEXT)
- proposedLatitude (DECIMAL)
- proposedLongitude (DECIMAL)
- proposedCity (STRING)
- proposedState (STRING)
- proposedPincode (STRING)
- distance (DECIMAL) - in kilometers
- reason (TEXT)
- effectiveDate (DATE)
- currentStage (ENUM: DD Admin Review, RBM Review, NBH Approval, Legal Clearance, Completed, Rejected)
- status (STRING)
- progressPercentage (INTEGER)
- submittedOn (DATE)
- documents (JSON)
- createdAt, updatedAt
7. worknotes
- id (UUID, PK)
- requestId (UUID) - Generic reference to any request
- requestType (ENUM: application, resignation, constitutional, relocation)
- userId (UUID, FK -> users)
- userName (STRING)
- userRole (STRING)
- message (TEXT)
- attachments (JSON)
- timestamp (DATE)
- createdAt, updatedAt
8. documents
- id (UUID, PK)
- filename (STRING)
- originalName (STRING)
- mimeType (STRING)
- size (INTEGER)
- path (STRING)
- uploadedBy (UUID, FK -> users)
- relatedTo (STRING) - application/resignation/etc
- relatedId (UUID)
- documentType (STRING) - GST, PAN, Aadhaar, etc
- uploadedAt (DATE)
- createdAt, updatedAt
9. audit_logs
- id (UUID, PK)
- userId (UUID, FK -> users)
- userName (STRING)
- userRole (STRING)
- action (STRING) - CREATED, UPDATED, APPROVED, REJECTED, etc
- entityType (STRING) - application, resignation, etc
- entityId (UUID)
- changes (JSON) - Before/after values
- ipAddress (STRING)
- timestamp (DATE)
- createdAt, updatedAt
10. finance_payments
- id (UUID, PK)
- applicationId (UUID, FK -> applications)
- outletId (UUID, FK -> outlets)
- dealerId (UUID, FK -> users)
- paymentType (ENUM: Security Deposit, License Fee, Setup Fee, Other)
- amount (DECIMAL)
- dueDate (DATE)
- paidDate (DATE)
- status (ENUM: Pending, Paid, Overdue, Waived)
- transactionId (STRING)
- paymentMode (STRING)
- remarks (TEXT)
- createdAt, updatedAt
11. fnf_settlements
- id (UUID, PK)
- fnfId (STRING, UNIQUE) - e.g., FNF-001
- resignationId (UUID, FK -> resignations)
- outletId (UUID, FK -> outlets)
- dealerId (UUID, FK -> users)
- totalDues (DECIMAL)
- securityDepositRefund (DECIMAL)
- otherCharges (DECIMAL)
- netSettlement (DECIMAL)
- status (ENUM: Initiated, DD Clearance, Legal Clearance, Finance Approval, Completed)
- settledDate (DATE)
- progressPercentage (INTEGER)
- createdAt, updatedAt
12. regions
- id (UUID, PK)
- name (STRING, UNIQUE) - East, West, North, South, Central
- code (STRING)
- headName (STRING)
- headEmail (STRING)
- isActive (BOOLEAN)
- createdAt, updatedAt
13. zones
- id (UUID, PK)
- name (STRING)
- code (STRING)
- regionId (UUID, FK -> regions)
- managerName (STRING)
- managerEmail (STRING)
- states (JSON) - Array of states covered
- isActive (BOOLEAN)
- createdAt, updatedAt
🔌 API Endpoints
Authentication
POST /api/auth/login
Request:
{
"email": "user@example.com",
"password": "password123"
}
Response:
{
"success": true,
"token": "jwt_token_here",
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"role": "DD"
}
}
POST /api/auth/logout
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"message": "Logged out successfully"
}
GET /api/auth/me
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"role": "DD",
"region": "West",
"zone": "Mumbai"
}
}
Outlets
GET /api/outlets/my-outlets
Get all outlets for logged-in dealer
Headers: { Authorization: "Bearer dealer_token" }
Response:
{
"success": true,
"outlets": [
{
"id": "uuid",
"code": "DL-MH-001",
"name": "Royal Enfield Mumbai",
"type": "Dealership",
"address": "Bandra West, Mumbai",
"status": "Active",
"hasActiveResignation": false
}
]
}
Resignations
POST /api/resignations/create
Create resignation request (Dealer only)
Headers: { Authorization: "Bearer dealer_token" }
Request:
{
"outletId": "uuid",
"resignationType": "Voluntary",
"lastOperationalDateSales": "2026-02-28",
"lastOperationalDateServices": "2026-02-28",
"reason": "Personal health issues",
"additionalInfo": "Optional details"
}
Response:
{
"success": true,
"resignationId": "RES-001",
"message": "Resignation request submitted successfully"
}
GET /api/resignations/list
Get resignations (role-based filtering)
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"resignations": [
{
"resignationId": "RES-001",
"outlet": { "code": "DL-MH-001", "name": "..." },
"resignationType": "Voluntary",
"currentStage": "ASM Review",
"status": "Pending",
"progressPercentage": 15
}
]
}
GET /api/resignations/:id
Get resignation details
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"resignation": {
"resignationId": "RES-001",
"outlet": {...},
"resignationType": "Voluntary",
"reason": "...",
"timeline": [...],
"worknotes": [...]
}
}
POST /api/resignations/:id/approve
Approve resignation at current stage
Headers: { Authorization: "Bearer jwt_token" }
Request:
{
"remarks": "Approved by ASM"
}
Response:
{
"success": true,
"message": "Resignation approved and moved to next stage"
}
POST /api/resignations/:id/reject
Reject resignation
Headers: { Authorization: "Bearer jwt_token" }
Request:
{
"reason": "Incomplete documentation"
}
Response:
{
"success": true,
"message": "Resignation rejected"
}
Constitutional Changes
POST /api/constitutional/create
Create constitutional change request (Dealer only)
Headers: { Authorization: "Bearer dealer_token" }
Request:
{
"outletId": "uuid",
"changeType": "Partnership Change",
"currentStructure": "Sole Proprietorship",
"proposedStructure": "Partnership Firm",
"reason": "Business expansion",
"effectiveDate": "2026-03-01"
}
Response:
{
"success": true,
"requestId": "CC-001"
}
GET /api/constitutional/list
Get constitutional change requests
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"requests": [...]
}
Relocation Requests
POST /api/relocations/create
Create relocation request (Dealer only)
Headers: { Authorization: "Bearer dealer_token" }
Request:
{
"outletId": "uuid",
"relocationType": "Within City",
"proposedAddress": "New location address",
"proposedLatitude": 19.0760,
"proposedLongitude": 72.8777,
"proposedCity": "Mumbai",
"proposedState": "Maharashtra",
"proposedPincode": "400050",
"reason": "Better footfall location",
"effectiveDate": "2026-04-01"
}
Response:
{
"success": true,
"requestId": "REL-001"
}
GET /api/relocations/calculate-distance
Calculate distance between two locations
Headers: { Authorization: "Bearer jwt_token" }
Query: ?fromLat=19.0760&fromLng=72.8777&toLat=19.1196&toLng=72.9046
Response:
{
"success": true,
"distance": 5.2,
"unit": "km"
}
Worknotes
POST /api/worknotes/create
Add worknote to any request
Headers: { Authorization: "Bearer jwt_token" }
Request:
{
"requestId": "uuid",
"requestType": "resignation",
"message": "Please provide updated documents"
}
Response:
{
"success": true,
"worknote": {...}
}
GET /api/worknotes/:requestId/:requestType
Get all worknotes for a request
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"worknotes": [
{
"id": "uuid",
"userName": "John Doe",
"userRole": "DD Admin",
"message": "...",
"timestamp": "2026-01-13T10:30:00Z"
}
]
}
Applications
POST /api/applications/create
Create new dealer application (public endpoint)
Request:
{
"applicantName": "Rajesh Kumar",
"email": "rajesh@example.com",
"phone": "+91-9876543210",
"businessType": "Dealership",
"proposedLocation": "Full address",
"city": "Mumbai",
"state": "Maharashtra",
"pincode": "400001"
}
Response:
{
"success": true,
"applicationId": "APP-2026-001"
}
GET /api/applications/list
Get applications (role-based filtering)
Headers: { Authorization: "Bearer jwt_token" }
Query: ?status=Pending®ion=West&page=1&limit=10
Response:
{
"success": true,
"applications": [...],
"total": 50,
"page": 1,
"pages": 5
}
POST /api/applications/:id/assign-ranking
DD assigns ranking (1-5)
Headers: { Authorization: "Bearer dd_token" }
Request:
{
"ranking": 4
}
Response:
{
"success": true,
"message": "Ranking assigned"
}
POST /api/applications/:id/move-stage
Move application to next stage
Headers: { Authorization: "Bearer jwt_token" }
Request:
{
"action": "approve", // or "reject"
"remarks": "All documents verified"
}
Response:
{
"success": true,
"nextStage": "DD-ZM"
}
Finance
GET /api/finance/onboarding-payments
Get all pending onboarding payments
Headers: { Authorization: "Bearer finance_token" }
Response:
{
"success": true,
"payments": [...]
}
POST /api/finance/payment/:id/mark-paid
Mark payment as received
Headers: { Authorization: "Bearer finance_token" }
Request:
{
"transactionId": "TXN123456",
"paidDate": "2026-01-13",
"paymentMode": "NEFT"
}
Response:
{
"success": true,
"message": "Payment marked as paid"
}
GET /api/finance/fnf-list
Get F&F settlement requests
Headers: { Authorization: "Bearer finance_token" }
Response:
{
"success": true,
"fnfRequests": [...]
}
Dashboard
GET /api/dashboard/stats
Get dashboard statistics (role-based)
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"stats": {
"totalApplications": 150,
"pendingApplications": 45,
"approvedApplications": 95,
"rejectedApplications": 10,
"pendingResignations": 5,
"pendingRelocations": 3
}
}
Master Configuration
GET /api/master/regions
Get all regions
Headers: { Authorization: "Bearer jwt_token" }
Response:
{
"success": true,
"regions": [
{
"id": "uuid",
"name": "East",
"code": "EAST",
"zones": [...]
}
]
}
POST /api/master/regions/create
Create region (Super Admin only)
Headers: { Authorization: "Bearer admin_token" }
Request:
{
"name": "East",
"code": "EAST",
"headName": "Amit Kumar",
"headEmail": "amit@example.com"
}
Response:
{
"success": true,
"region": {...}
}
File Upload
POST /api/upload/document
Upload document
Headers: {
Authorization: "Bearer jwt_token",
Content-Type: "multipart/form-data"
}
FormData:
- file: (binary)
- relatedTo: "resignation"
- relatedId: "uuid"
- documentType: "GST Certificate"
Response:
{
"success": true,
"document": {
"id": "uuid",
"filename": "unique-filename.pdf",
"originalName": "gst-certificate.pdf",
"url": "/uploads/documents/unique-filename.pdf"
}
}
🔐 Authentication & Authorization
JWT Authentication Flow:
- Login: User sends credentials → Server validates → Returns JWT token
- Protected Routes: Client sends token in Authorization header
- Token Verification: Middleware validates token before processing request
- Token Expiry: 24 hours (configurable)
Role-Based Access Control (RBAC):
Middleware checks user role against route permissions:
// Example: Only DD Lead can access opportunity requests
router.get('/opportunity-requests',
auth,
roleCheck(['DD Lead']),
getOpportunityRequests
);
// Example: Multiple roles can approve resignations
router.post('/resignations/:id/approve',
auth,
roleCheck(['DD Admin', 'RBM', 'ZBH', 'NBH', 'Legal Admin']),
approveResignation
);
Permission Matrix:
| Feature | DD | DD-ZM | RBM | ZBH | DD Lead | DD Head | NBH | DD Admin | Legal | Finance | Dealer |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Create Application | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| View All Applications | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Approve Application Stage | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
| Create Resignation | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Approve Resignation | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Constitutional Change | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Relocation Request | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
| Master Configuration | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
📧 Email Notifications
Email Triggers:
- Application Submitted → Notify assigned DD
- Application Approved/Rejected → Notify applicant
- Stage Changed → Notify next approver
- Deadline Approaching → Reminder email (3 days, 1 day before)
- Resignation Submitted → Notify DD Admin
- Constitutional Change → Notify Legal team
- Relocation Request → Notify RBM
- Payment Due → Notify dealer
- F&F Initiated → Notify Finance team
Email Templates:
Located in utils/emailTemplates.js:
- Application confirmation
- Approval notification
- Rejection notification
- Stage progression
- Deadline reminder
- Password reset
🚀 Setup Instructions
1. Prerequisites
Install the following on your system:
- Node.js v18+ (https://nodejs.org)
- PostgreSQL 14+ (https://www.postgresql.org)
- Git (optional)
2. Install Dependencies
cd backend
npm install
3. Database Setup
Create PostgreSQL database:
CREATE DATABASE royal_enfield_onboarding;
CREATE USER re_admin WITH PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE royal_enfield_onboarding TO re_admin;
4. Environment Configuration
Copy .env.example to .env and configure:
# Server
NODE_ENV=development
PORT=5000
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=royal_enfield_onboarding
DB_USER=re_admin
DB_PASSWORD=your_password
# JWT
JWT_SECRET=your_super_secret_jwt_key_here_make_it_long_and_random
JWT_EXPIRES_IN=24h
# Email (Gmail example)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=your-email@gmail.com
EMAIL_PASSWORD=your-app-specific-password
EMAIL_FROM=Royal Enfield <noreply@royalenfield.com>
# File Upload
UPLOAD_PATH=./uploads
MAX_FILE_SIZE=10485760
# Frontend URL (for CORS)
FRONTEND_URL=http://localhost:5173
# Admin Default Password (for initial setup)
ADMIN_DEFAULT_PASSWORD=Admin@123
5. Run Migrations
npm run migrate
This creates all tables and seeds initial data:
- Super Admin user
- 5 Regions (East, West, North, South, Central)
- Sample zones
- Sample users for each role
6. Start Server
Development:
npm run dev
Production:
npm start
Server runs on: http://localhost:5000
7. Test API
Use Postman/Thunder Client:
# Login as Super Admin
POST http://localhost:5000/api/auth/login
Body: {
"email": "admin@royalenfield.com",
"password": "Admin@123"
}
# Get dashboard stats
GET http://localhost:5000/api/dashboard/stats
Headers: Authorization: Bearer <token_from_login>
🔗 Frontend Integration
Update Frontend API Calls
In your frontend, update API base URL:
Create /src/lib/api.ts:
const API_BASE_URL = process.env.NODE_ENV === 'production'
? 'https://your-backend-domain.com/api'
: 'http://localhost:5000/api';
export const api = {
async login(email: string, password: string) {
const response = await fetch(`${API_BASE_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
return response.json();
},
async getMyOutlets(token: string) {
const response = await fetch(`${API_BASE_URL}/outlets/my-outlets`, {
headers: { 'Authorization': `Bearer ${token}` }
});
return response.json();
},
async createResignation(token: string, data: any) {
const response = await fetch(`${API_BASE_URL}/resignations/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(data)
});
return response.json();
}
};
Replace Mock Data
Before (Mock):
const mockOutlets = [...]; // Hardcoded data
After (Real API):
useEffect(() => {
const fetchOutlets = async () => {
const token = localStorage.getItem('token');
const result = await api.getMyOutlets(token);
setOutlets(result.outlets);
};
fetchOutlets();
}, []);
📊 Workflow State Machines
Application Workflow:
Initial → DD Review → DD-ZM Review → RBM Review → ZBH Review
→ DD Lead Review → DD Head Review → NBH Approval → Legal Clearance
→ Finance Payment → Approved ✅
Resignation Workflow:
Submitted → ASM Review → RBM Review → ZBH Review → NBH Approval
→ DD Admin Clearance → Legal Clearance → F&F Initiation
→ Finance Settlement → Completed ✅
Constitutional Change Workflow:
Submitted → DD Admin Review → Legal Verification → NBH Approval
→ Finance Clearance → Completed ✅
Relocation Workflow:
Submitted → DD Admin Review → RBM Assessment → NBH Approval
→ Legal Clearance → Completed ✅
🛡️ Security Features
- Password Hashing: bcryptjs with salt rounds
- JWT Tokens: Secure token-based authentication
- CORS: Configured for frontend domain only
- Helmet: Security headers
- Rate Limiting: Prevent brute force attacks
- Input Validation: express-validator on all inputs
- SQL Injection Prevention: Sequelize ORM parameterized queries
- File Upload Validation: Type and size restrictions
- Audit Logging: All actions logged with user/timestamp
📝 Logging
Winston logger with multiple transports:
- Console: Development logging
- File:
logs/error.log- Error logs - File:
logs/combined.log- All logs
🧪 Testing
# Run tests
npm test
# Run with coverage
npm run test:coverage
🚢 Deployment
Option 1: Railway (Recommended)
- Create account on railway.app
- Connect GitHub repository
- Add PostgreSQL plugin
- Set environment variables
- Deploy automatically
Option 2: Render
- Create account on render.com
- Create Web Service
- Connect repository
- Add PostgreSQL database
- Set environment variables
- Deploy
Option 3: AWS/DigitalOcean
- Set up EC2/Droplet
- Install Node.js, PostgreSQL
- Clone repository
- Configure environment
- Use PM2 for process management
- Set up Nginx reverse proxy
Environment Variables for Production:
NODE_ENV=production
DB_HOST=<production-db-host>
DB_NAME=<production-db-name>
DB_USER=<production-db-user>
DB_PASSWORD=<production-db-password>
JWT_SECRET=<strong-random-secret>
EMAIL_HOST=<smtp-host>
EMAIL_USER=<email>
EMAIL_PASSWORD=<password>
FRONTEND_URL=https://your-frontend-domain.com
📦 Dependencies
Core:
- express: Web framework
- sequelize: ORM
- pg, pg-hstore: PostgreSQL driver
- jsonwebtoken: JWT authentication
- bcryptjs: Password hashing
Middleware:
- cors: Cross-origin resource sharing
- helmet: Security headers
- express-validator: Input validation
- multer: File uploads
Utilities:
- nodemailer: Email sending
- winston: Logging
- dotenv: Environment variables
- uuid: Unique IDs
Dev Dependencies:
- nodemon: Auto-restart on changes
- jest: Testing framework
🔧 Maintenance
Database Backup:
pg_dump -U re_admin royal_enfield_onboarding > backup.sql
Restore Database:
psql -U re_admin royal_enfield_onboarding < backup.sql
Clear Logs:
npm run clear-logs
📞 Support & Troubleshooting
Common Issues:
1. Database Connection Error:
- Check PostgreSQL is running:
sudo service postgresql status - Verify credentials in
.env - Check database exists:
psql -l
2. Port Already in Use:
- Change PORT in
.env - Kill process:
lsof -ti:5000 | xargs kill
3. JWT Token Invalid:
- Check JWT_SECRET is same across restarts
- Verify token expiry time
- Clear old tokens from frontend
4. File Upload Failing:
- Check UPLOAD_PATH directory exists and is writable
- Verify MAX_FILE_SIZE setting
- Check disk space
5. Email Not Sending:
- Verify SMTP credentials
- Check firewall/port 587 access
- Enable "Less secure apps" for Gmail or use App Password
🎯 Next Steps for AI Assistant (Cursor/Windsurf/etc)
When you provide this backend to your AI IDE, it will understand:
- ✅ Complete architecture and file structure
- ✅ Database schema and relationships
- ✅ All API endpoints and their contracts
- ✅ Authentication and authorization flow
- ✅ Environment setup requirements
- ✅ Deployment options
- ✅ Integration points with frontend
The AI can then:
- Help you set up the database
- Debug any issues
- Add new features
- Optimize queries
- Handle deployment
- Write tests
- Generate documentation
📄 License
Proprietary - Royal Enfield Dealership Onboarding System
Created: January 2026
Version: 1.0.0
Last Updated: January 13, 2026
For questions or support, refer to this documentation first, then consult with your development team.