1271 lines
29 KiB
Markdown
1271 lines
29 KiB
Markdown
# 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:
|
|
|
|
1. **DD (Dealer Development)** - Creates applications, initial review
|
|
2. **DD-ZM (DD Zone Manager)** - Zone-level approvals
|
|
3. **RBM (Regional Business Manager)** - Regional approvals
|
|
4. **ZBH (Zonal Business Head)** - Zonal head approvals
|
|
5. **DD Lead** - Organization-wide DD leadership (singular position)
|
|
6. **DD Head** - Head of DD department (singular position)
|
|
7. **NBH (National Business Head)** - National head (singular position)
|
|
8. **DD Admin** - Administrative operations
|
|
9. **Legal Admin** - Legal clearance and document verification
|
|
10. **Super Admin** - Full system access
|
|
11. **DD AM (Area Manager)** - Area-level management
|
|
12. **Finance** - Payment tracking and financial approvals
|
|
13. **Dealer** - Dealer portal access (resignation, relocation, constitutional changes)
|
|
|
|
## 🗄️ Database Schema
|
|
|
|
### Key Tables:
|
|
|
|
#### 1. **users**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- 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**
|
|
```sql
|
|
- id (UUID, PK)
|
|
- name (STRING, UNIQUE) - East, West, North, South, Central
|
|
- code (STRING)
|
|
- headName (STRING)
|
|
- headEmail (STRING)
|
|
- isActive (BOOLEAN)
|
|
- createdAt, updatedAt
|
|
```
|
|
|
|
#### 13. **zones**
|
|
```sql
|
|
- 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`
|
|
```json
|
|
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`
|
|
```json
|
|
Headers: { Authorization: "Bearer jwt_token" }
|
|
|
|
Response:
|
|
{
|
|
"success": true,
|
|
"message": "Logged out successfully"
|
|
}
|
|
```
|
|
|
|
#### `GET /api/auth/me`
|
|
```json
|
|
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
|
|
```json
|
|
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)
|
|
```json
|
|
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)
|
|
```json
|
|
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
|
|
```json
|
|
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
|
|
```json
|
|
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
|
|
```json
|
|
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)
|
|
```json
|
|
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
|
|
```json
|
|
Headers: { Authorization: "Bearer jwt_token" }
|
|
|
|
Response:
|
|
{
|
|
"success": true,
|
|
"requests": [...]
|
|
}
|
|
```
|
|
|
|
### Relocation Requests
|
|
|
|
#### `POST /api/relocations/create`
|
|
Create relocation request (Dealer only)
|
|
```json
|
|
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
|
|
```json
|
|
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
|
|
```json
|
|
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
|
|
```json
|
|
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)
|
|
```json
|
|
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)
|
|
```json
|
|
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)
|
|
```json
|
|
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
|
|
```json
|
|
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
|
|
```json
|
|
Headers: { Authorization: "Bearer finance_token" }
|
|
|
|
Response:
|
|
{
|
|
"success": true,
|
|
"payments": [...]
|
|
}
|
|
```
|
|
|
|
#### `POST /api/finance/payment/:id/mark-paid`
|
|
Mark payment as received
|
|
```json
|
|
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
|
|
```json
|
|
Headers: { Authorization: "Bearer finance_token" }
|
|
|
|
Response:
|
|
{
|
|
"success": true,
|
|
"fnfRequests": [...]
|
|
}
|
|
```
|
|
|
|
### Dashboard
|
|
|
|
#### `GET /api/dashboard/stats`
|
|
Get dashboard statistics (role-based)
|
|
```json
|
|
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
|
|
```json
|
|
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)
|
|
```json
|
|
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
|
|
```json
|
|
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:
|
|
|
|
1. **Login:** User sends credentials → Server validates → Returns JWT token
|
|
2. **Protected Routes:** Client sends token in Authorization header
|
|
3. **Token Verification:** Middleware validates token before processing request
|
|
4. **Token Expiry:** 24 hours (configurable)
|
|
|
|
### Role-Based Access Control (RBAC):
|
|
|
|
Middleware checks user role against route permissions:
|
|
|
|
```javascript
|
|
// 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:
|
|
|
|
1. **Application Submitted** → Notify assigned DD
|
|
2. **Application Approved/Rejected** → Notify applicant
|
|
3. **Stage Changed** → Notify next approver
|
|
4. **Deadline Approaching** → Reminder email (3 days, 1 day before)
|
|
5. **Resignation Submitted** → Notify DD Admin
|
|
6. **Constitutional Change** → Notify Legal team
|
|
7. **Relocation Request** → Notify RBM
|
|
8. **Payment Due** → Notify dealer
|
|
9. **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
|
|
|
|
```bash
|
|
cd backend
|
|
npm install
|
|
```
|
|
|
|
### 3. Database Setup
|
|
|
|
Create PostgreSQL database:
|
|
```sql
|
|
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:
|
|
|
|
```env
|
|
# 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
|
|
|
|
```bash
|
|
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:**
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
**Production:**
|
|
```bash
|
|
npm start
|
|
```
|
|
|
|
Server runs on: `http://localhost:5000`
|
|
|
|
### 7. Test API
|
|
|
|
Use Postman/Thunder Client:
|
|
|
|
```bash
|
|
# 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`:**
|
|
```typescript
|
|
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):**
|
|
```typescript
|
|
const mockOutlets = [...]; // Hardcoded data
|
|
```
|
|
|
|
**After (Real API):**
|
|
```typescript
|
|
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
|
|
|
|
1. **Password Hashing:** bcryptjs with salt rounds
|
|
2. **JWT Tokens:** Secure token-based authentication
|
|
3. **CORS:** Configured for frontend domain only
|
|
4. **Helmet:** Security headers
|
|
5. **Rate Limiting:** Prevent brute force attacks
|
|
6. **Input Validation:** express-validator on all inputs
|
|
7. **SQL Injection Prevention:** Sequelize ORM parameterized queries
|
|
8. **File Upload Validation:** Type and size restrictions
|
|
9. **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
|
|
|
|
```bash
|
|
# Run tests
|
|
npm test
|
|
|
|
# Run with coverage
|
|
npm run test:coverage
|
|
```
|
|
|
|
## 🚢 Deployment
|
|
|
|
### Option 1: Railway (Recommended)
|
|
|
|
1. Create account on railway.app
|
|
2. Connect GitHub repository
|
|
3. Add PostgreSQL plugin
|
|
4. Set environment variables
|
|
5. Deploy automatically
|
|
|
|
### Option 2: Render
|
|
|
|
1. Create account on render.com
|
|
2. Create Web Service
|
|
3. Connect repository
|
|
4. Add PostgreSQL database
|
|
5. Set environment variables
|
|
6. Deploy
|
|
|
|
### Option 3: AWS/DigitalOcean
|
|
|
|
1. Set up EC2/Droplet
|
|
2. Install Node.js, PostgreSQL
|
|
3. Clone repository
|
|
4. Configure environment
|
|
5. Use PM2 for process management
|
|
6. Set up Nginx reverse proxy
|
|
|
|
### Environment Variables for Production:
|
|
|
|
```env
|
|
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:
|
|
```bash
|
|
pg_dump -U re_admin royal_enfield_onboarding > backup.sql
|
|
```
|
|
|
|
### Restore Database:
|
|
```bash
|
|
psql -U re_admin royal_enfield_onboarding < backup.sql
|
|
```
|
|
|
|
### Clear Logs:
|
|
```bash
|
|
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:
|
|
|
|
1. ✅ Complete architecture and file structure
|
|
2. ✅ Database schema and relationships
|
|
3. ✅ All API endpoints and their contracts
|
|
4. ✅ Authentication and authorization flow
|
|
5. ✅ Environment setup requirements
|
|
6. ✅ Deployment options
|
|
7. ✅ 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.
|