dealer seed added and api created based on dealer table

This commit is contained in:
laxmanhalaki 2025-12-24 20:41:22 +05:30
parent 9c8a8512bc
commit 855cfc5c38
5 changed files with 398 additions and 90 deletions

View File

@ -18,6 +18,7 @@
"setup": "ts-node -r tsconfig-paths/register src/scripts/auto-setup.ts",
"migrate": "ts-node -r tsconfig-paths/register src/scripts/migrate.ts",
"seed:config": "ts-node -r tsconfig-paths/register src/scripts/seed-admin-config.ts",
"seed:test-dealer": "ts-node -r tsconfig-paths/register src/scripts/seed-test-dealer.ts",
"cleanup:dealer-claims": "ts-node -r tsconfig-paths/register src/scripts/cleanup-dealer-claims.ts"
},
"dependencies": {

View File

@ -7,11 +7,15 @@ import logger from '../utils/logger';
export class DealerController {
/**
* Get all dealers
* GET /api/v1/dealers
* GET /api/v1/dealers?q=searchTerm&limit=10 (optional search and limit)
*/
async getAllDealers(req: Request, res: Response): Promise<void> {
try {
const dealers = await dealerService.getAllDealers();
const searchTerm = req.query.q as string | undefined;
const limitParam = req.query.limit as string | undefined;
// Parse limit, default to 10, max 100
const limit = limitParam ? Math.min(Math.max(1, parseInt(limitParam, 10)), 100) : 10;
const dealers = await dealerService.getAllDealers(searchTerm, limit);
return ResponseHandler.success(res, dealers, 'Dealers fetched successfully');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
@ -64,17 +68,19 @@ export class DealerController {
/**
* Search dealers
* GET /api/v1/dealers/search?q=searchTerm
* GET /api/v1/dealers/search?q=searchTerm&limit=10
*/
async searchDealers(req: Request, res: Response): Promise<void> {
try {
const { q } = req.query;
const { q, limit: limitParam } = req.query;
if (!q || typeof q !== 'string') {
return ResponseHandler.error(res, 'Search term is required', 400);
}
const dealers = await dealerService.searchDealers(q);
// Parse limit, default to 10, max 100
const limit = limitParam ? Math.min(Math.max(1, parseInt(limitParam as string, 10)), 100) : 10;
const dealers = await dealerService.searchDealers(q, limit);
return ResponseHandler.success(res, dealers, 'Dealers searched successfully');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
@ -82,5 +88,36 @@ export class DealerController {
return ResponseHandler.error(res, 'Failed to search dealers', 500, errorMessage);
}
}
/**
* Verify dealer is logged in
* GET /api/v1/dealers/verify/:dealerCode
* Returns dealer info with isLoggedIn flag
*/
async verifyDealerLogin(req: Request, res: Response): Promise<void> {
try {
const { dealerCode } = req.params;
const dealer = await dealerService.getDealerByCode(dealerCode);
if (!dealer) {
return ResponseHandler.error(res, 'Dealer not found', 404);
}
if (!dealer.isLoggedIn) {
return ResponseHandler.error(
res,
'Dealer not logged in to the system',
400,
`The dealer with code ${dealerCode} (${dealer.email}) has not logged in to the system. Please ask them to log in first.`
);
}
return ResponseHandler.success(res, dealer, 'Dealer verified and logged in');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
logger.error('[DealerController] Error verifying dealer login:', error);
return ResponseHandler.error(res, 'Failed to verify dealer', 500, errorMessage);
}
}
}

View File

@ -34,5 +34,12 @@ router.get('/code/:dealerCode', authenticateToken, asyncHandler(dealerController
*/
router.get('/email/:email', authenticateToken, asyncHandler(dealerController.getDealerByEmail.bind(dealerController)));
/**
* @route GET /api/v1/dealers/verify/:dealerCode
* @desc Verify dealer is logged in to the system
* @access Private
*/
router.get('/verify/:dealerCode', authenticateToken, asyncHandler(dealerController.verifyDealerLogin.bind(dealerController)));
export default router;

View File

@ -0,0 +1,179 @@
/**
* Seed Test Dealer
* Creates a test dealer record in the dealers table for testing purposes
*
* This creates a dealer with:
* - domain_id: testreflow@example.com
* - dealer_principal_email_id: testreflow@example.com
* - dealer_principal_name: TEST REFLOW
* - Random codes for sales_code, gear_code, and dlrcode to avoid conflicts
*/
import { sequelize } from '../config/database';
import { Dealer } from '../models/Dealer';
import { Op } from 'sequelize';
import logger from '../utils/logger';
/**
* Generate a random 4-digit code
* Checks for existing codes to avoid conflicts
*/
async function generateUniqueCode(
field: 'salesCode' | 'serviceCode' | 'gearCode' | 'gmaCode' | 'dlrcode',
existingCodes: Set<string>
): Promise<string> {
let attempts = 0;
const maxAttempts = 100;
while (attempts < maxAttempts) {
// Generate random 4-digit number (1000-9999)
const randomCode = String(Math.floor(1000 + Math.random() * 9000));
// Check if code already exists in database
const existing = await Dealer.findOne({
where: {
[field]: randomCode
}
});
// Also check if we've already generated this code in this run
if (!existing && !existingCodes.has(randomCode)) {
existingCodes.add(randomCode);
return randomCode;
}
attempts++;
}
// Fallback: use timestamp-based code if random generation fails
const timestampCode = String(Date.now()).slice(-4);
logger.warn(`[Seed Test Dealer] Using timestamp-based code for ${field}: ${timestampCode}`);
return timestampCode;
}
async function seedTestDealer(): Promise<void> {
try {
logger.info('[Seed Test Dealer] Starting test dealer seeding...');
// Check if test dealer already exists
const existingDealer = await Dealer.findOne({
where: {
[Op.or]: [
{ domainId: 'testreflow@example.com' },
{ dealerPrincipalEmailId: 'testreflow@example.com' }
]
}
});
// Generate unique codes
const existingCodes = new Set<string>();
const salesCode = await generateUniqueCode('salesCode', existingCodes);
const serviceCode = await generateUniqueCode('serviceCode', existingCodes);
const gearCode = await generateUniqueCode('gearCode', existingCodes);
const gmaCode = await generateUniqueCode('gmaCode', existingCodes);
const dlrcode = await generateUniqueCode('dlrcode', existingCodes);
const dealerData = {
salesCode,
serviceCode,
gearCode,
gmaCode,
region: 'TEST',
dealership: 'TEST REFLOW DEALERSHIP',
state: 'Test State',
district: 'Test District',
city: 'Test City',
location: 'Test Location',
cityCategoryPst: 'A',
layoutFormat: 'A',
tierCityCategory: 'Tier 1 City',
onBoardingCharges: null,
date: new Date().toISOString().split('T')[0], // Current date in YYYY-MM-DD format
singleFormatMonthYear: (() => {
const now = new Date();
const month = now.toLocaleDateString('en-US', { month: 'short' });
const year = now.getFullYear();
return `${month}-${year}`; // e.g., "Dec-2025" (format: "MMM-YYYY")
})(),
domainId: 'testreflow@example.com',
replacement: null,
terminationResignationStatus: null,
dateOfTerminationResignation: null,
lastDateOfOperations: null,
oldCodes: null,
branchDetails: null,
dealerPrincipalName: 'TEST REFLOW',
dealerPrincipalEmailId: 'testreflow@example.com',
dpContactNumber: null,
dpContacts: null,
showroomAddress: null,
showroomPincode: null,
workshopAddress: null,
workshopPincode: null,
locationDistrict: null,
stateWorkshop: null,
noOfStudios: 0,
websiteUpdate: 'Yes',
gst: null,
pan: null,
firmType: 'Test Firm',
propManagingPartnersDirectors: 'TEST REFLOW',
totalPropPartnersDirectors: 'TEST REFLOW',
docsFolderLink: null,
workshopGmaCodes: null,
existingNew: 'New',
dlrcode,
isActive: true
};
if (existingDealer) {
logger.info('[Seed Test Dealer] Test dealer already exists, updating...');
// Update existing dealer
await existingDealer.update(dealerData);
logger.info(`[Seed Test Dealer] ✅ Updated test dealer: ${existingDealer.dealerId}`);
logger.info(`[Seed Test Dealer] - Domain ID: ${dealerData.domainId}`);
logger.info(`[Seed Test Dealer] - Dealer Principal: ${dealerData.dealerPrincipalName}`);
logger.info(`[Seed Test Dealer] - Sales Code: ${dealerData.salesCode}`);
logger.info(`[Seed Test Dealer] - Gear Code: ${dealerData.gearCode}`);
logger.info(`[Seed Test Dealer] - Dealer Code (dlrcode): ${dealerData.dlrcode}`);
} else {
// Create new dealer
const newDealer = await Dealer.create(dealerData);
logger.info(`[Seed Test Dealer] ✅ Created test dealer: ${newDealer.dealerId}`);
logger.info(`[Seed Test Dealer] - Domain ID: ${dealerData.domainId}`);
logger.info(`[Seed Test Dealer] - Dealer Principal: ${dealerData.dealerPrincipalName}`);
logger.info(`[Seed Test Dealer] - Sales Code: ${dealerData.salesCode}`);
logger.info(`[Seed Test Dealer] - Gear Code: ${dealerData.gearCode}`);
logger.info(`[Seed Test Dealer] - Dealer Code (dlrcode): ${dealerData.dlrcode}`);
}
logger.info('[Seed Test Dealer] ✅ Test dealer seeding completed successfully');
} catch (error) {
logger.error('[Seed Test Dealer] ❌ Error seeding test dealer:', error);
throw error;
}
}
// Run if called directly
if (require.main === module) {
sequelize
.authenticate()
.then(() => {
logger.info('[Seed Test Dealer] Database connection established');
return seedTestDealer();
})
.then(() => {
logger.info('[Seed Test Dealer] Seeding completed');
process.exit(0);
})
.catch((error) => {
logger.error('[Seed Test Dealer] Seeding failed:', error);
process.exit(1);
});
}
export { seedTestDealer };

View File

@ -1,55 +1,111 @@
/**
* Dealer Service
* Handles dealer-related operations for claim management
* Fetches from dealers table and checks if dealer is logged in (domain_id exists in users table)
*/
import { User } from '../models/User';
import { Dealer } from '../models/Dealer';
import { Op } from 'sequelize';
import logger from '../utils/logger';
export interface DealerInfo {
userId: string;
email: string;
dealerCode: string;
dealerName: string;
displayName: string;
phone?: string;
dealerId: string;
userId?: string | null; // User ID if dealer is logged in
email: string; // domain_id from dealers table
dealerCode: string; // dlrcode from dealers table
dealerName: string; // dealership from dealers table
displayName: string; // dealer_principal_name from dealers table
phone?: string; // dp_contact_number from dealers table
department?: string;
designation?: string;
isLoggedIn: boolean; // Whether dealer's domain_id exists in users table
salesCode?: string | null;
serviceCode?: string | null;
gearCode?: string | null;
gmaCode?: string | null;
region?: string | null;
state?: string | null;
district?: string | null;
city?: string | null;
dealerPrincipalName?: string | null;
dealerPrincipalEmailId?: string | null;
}
/**
* Get all dealers (users with designation = 'dealer')
* Note: If employeeId is empty, generates a dummy dealerCode like "RE-MH-001" in the response
* The database employeeId remains unchanged - this is only for the API response
* Get all dealers from dealers table
* Checks if dealer is logged in by matching domain_id with users.email
* @param searchTerm - Optional search term to filter dealers
* @param limit - Maximum number of records to return (default: 10)
*/
export async function getAllDealers(): Promise<DealerInfo[]> {
export async function getAllDealers(searchTerm?: string, limit: number = 10): Promise<DealerInfo[]> {
try {
const dealers = await User.findAll({
where: {
designation: { [Op.iLike]: 'dealer' } as any,
isActive: true,
},
order: [['displayName', 'ASC']],
// Build where clause for search
const whereClause: any = {
isActive: true,
};
if (searchTerm && searchTerm.trim()) {
whereClause[Op.or] = [
{ dealership: { [Op.iLike]: `%${searchTerm}%` } as any },
{ dealerPrincipalName: { [Op.iLike]: `%${searchTerm}%` } as any },
{ domainId: { [Op.iLike]: `%${searchTerm}%` } as any },
{ dlrcode: { [Op.iLike]: `%${searchTerm}%` } as any },
{ salesCode: { [Op.iLike]: `%${searchTerm}%` } as any },
{ gearCode: { [Op.iLike]: `%${searchTerm}%` } as any },
];
}
const dealers = await Dealer.findAll({
where: whereClause,
order: [['dealership', 'ASC']],
limit: limit, // Always limit results to specified limit (default 10)
});
return dealers.map((dealer, index) => {
// Generate dummy dealerCode in response if employeeId is empty
// Format: RE-MH-XXX where XXX is a zero-padded index (001, 002, etc.)
// This is only for the API response - database employeeId is not modified
const dealerCode = dealer.employeeId && dealer.employeeId.trim() !== ''
? dealer.employeeId
: `RE-MH-${String(index + 1).padStart(3, '0')}`;
// Get all domain_ids to check which dealers are logged in
const domainIds = dealers
.map((d) => d.domainId)
.filter((id): id is string => id !== null && id !== undefined);
// Check which domain_ids exist in users table
const loggedInUsers = await User.findAll({
where: {
email: { [Op.in]: domainIds } as any,
isActive: true,
},
attributes: ['userId', 'email', 'displayName', 'phone', 'department', 'designation'],
});
// Create a map of email -> user for quick lookup
const userMap = new Map(loggedInUsers.map((u) => [u.email.toLowerCase(), u]));
// Map dealers to DealerInfo with login status
return dealers.map((dealer) => {
const domainId = dealer.domainId?.toLowerCase() || '';
const user = domainId ? userMap.get(domainId) : null;
const isLoggedIn = !!user;
return {
userId: dealer.userId,
email: dealer.email,
dealerCode: dealerCode,
dealerName: dealer.displayName || dealer.email,
displayName: dealer.displayName || dealer.email,
phone: dealer.phone || undefined,
department: dealer.department || undefined,
designation: dealer.designation || undefined,
dealerId: dealer.dealerId,
userId: user?.userId || null,
email: dealer.domainId || '',
dealerCode: dealer.dlrcode || '',
dealerName: dealer.dealership || dealer.dealerPrincipalName || '',
displayName: dealer.dealerPrincipalName || dealer.dealership || '',
phone: dealer.dpContactNumber || undefined,
department: user?.department || undefined,
designation: user?.designation || undefined,
isLoggedIn,
salesCode: dealer.salesCode || null,
serviceCode: dealer.serviceCode || null,
gearCode: dealer.gearCode || null,
gmaCode: dealer.gmaCode || null,
region: dealer.region || null,
state: dealer.state || null,
district: dealer.district || null,
city: dealer.city || null,
dealerPrincipalName: dealer.dealerPrincipalName || null,
dealerPrincipalEmailId: dealer.dealerPrincipalEmailId || null,
};
});
} catch (error) {
@ -59,14 +115,14 @@ export async function getAllDealers(): Promise<DealerInfo[]> {
}
/**
* Get dealer by code
* Get dealer by code (dlrcode)
* Checks if dealer is logged in by matching domain_id with users.email
*/
export async function getDealerByCode(dealerCode: string): Promise<DealerInfo | null> {
try {
const dealer = await User.findOne({
const dealer = await Dealer.findOne({
where: {
employeeId: dealerCode,
designation: { [Op.iLike]: 'dealer' } as any,
dlrcode: dealerCode,
isActive: true,
},
});
@ -75,15 +131,41 @@ export async function getDealerByCode(dealerCode: string): Promise<DealerInfo |
return null;
}
// Check if dealer is logged in (domain_id exists in users table)
let user = null;
if (dealer.domainId) {
user = await User.findOne({
where: {
email: dealer.domainId.toLowerCase(),
isActive: true,
},
attributes: ['userId', 'email', 'displayName', 'phone', 'department', 'designation'],
});
}
const isLoggedIn = !!user;
return {
userId: dealer.userId,
email: dealer.email,
dealerCode: dealer.employeeId || '',
dealerName: dealer.displayName || dealer.email,
displayName: dealer.displayName || dealer.email,
phone: dealer.phone || undefined,
department: dealer.department || undefined,
designation: dealer.designation || undefined,
dealerId: dealer.dealerId,
userId: user?.userId || null,
email: dealer.domainId || '',
dealerCode: dealer.dlrcode || '',
dealerName: dealer.dealership || dealer.dealerPrincipalName || '',
displayName: dealer.dealerPrincipalName || dealer.dealership || '',
phone: dealer.dpContactNumber || user?.phone || undefined,
department: user?.department || undefined,
designation: user?.designation || undefined,
isLoggedIn,
salesCode: dealer.salesCode || null,
serviceCode: dealer.serviceCode || null,
gearCode: dealer.gearCode || null,
gmaCode: dealer.gmaCode || null,
region: dealer.region || null,
state: dealer.state || null,
district: dealer.district || null,
city: dealer.city || null,
dealerPrincipalName: dealer.dealerPrincipalName || null,
dealerPrincipalEmailId: dealer.dealerPrincipalEmailId || null,
};
} catch (error) {
logger.error('[DealerService] Error fetching dealer by code:', error);
@ -92,14 +174,14 @@ export async function getDealerByCode(dealerCode: string): Promise<DealerInfo |
}
/**
* Get dealer by email
* Get dealer by email (domain_id)
* Checks if dealer is logged in by matching domain_id with users.email
*/
export async function getDealerByEmail(email: string): Promise<DealerInfo | null> {
try {
const dealer = await User.findOne({
const dealer = await Dealer.findOne({
where: {
email: email.toLowerCase(),
designation: { [Op.iLike]: 'dealer' } as any,
domainId: { [Op.iLike]: email.toLowerCase() } as any,
isActive: true,
},
});
@ -108,15 +190,41 @@ export async function getDealerByEmail(email: string): Promise<DealerInfo | null
return null;
}
// Check if dealer is logged in (domain_id exists in users table)
let user = null;
if (dealer.domainId) {
user = await User.findOne({
where: {
email: dealer.domainId.toLowerCase(),
isActive: true,
},
attributes: ['userId', 'email', 'displayName', 'phone', 'department', 'designation'],
});
}
const isLoggedIn = !!user;
return {
userId: dealer.userId,
email: dealer.email,
dealerCode: dealer.employeeId || '',
dealerName: dealer.displayName || dealer.email,
displayName: dealer.displayName || dealer.email,
phone: dealer.phone || undefined,
department: dealer.department || undefined,
designation: dealer.designation || undefined,
dealerId: dealer.dealerId,
userId: user?.userId || null,
email: dealer.domainId || '',
dealerCode: dealer.dlrcode || '',
dealerName: dealer.dealership || dealer.dealerPrincipalName || '',
displayName: dealer.dealerPrincipalName || dealer.dealership || '',
phone: dealer.dpContactNumber || user?.phone || undefined,
department: user?.department || undefined,
designation: user?.designation || undefined,
isLoggedIn,
salesCode: dealer.salesCode || null,
serviceCode: dealer.serviceCode || null,
gearCode: dealer.gearCode || null,
gmaCode: dealer.gmaCode || null,
region: dealer.region || null,
state: dealer.state || null,
district: dealer.district || null,
city: dealer.city || null,
dealerPrincipalName: dealer.dealerPrincipalName || null,
dealerPrincipalEmailId: dealer.dealerPrincipalEmailId || null,
};
} catch (error) {
logger.error('[DealerService] Error fetching dealer by email:', error);
@ -125,38 +233,14 @@ export async function getDealerByEmail(email: string): Promise<DealerInfo | null
}
/**
* Search dealers by name or code
* Search dealers by name, code, or email
* Uses getAllDealers with search term
* @param searchTerm - Search term to filter dealers
* @param limit - Maximum number of records to return (default: 10)
*/
export async function searchDealers(searchTerm: string): Promise<DealerInfo[]> {
export async function searchDealers(searchTerm: string, limit: number = 10): Promise<DealerInfo[]> {
try {
const dealers = await User.findAll({
where: {
[Op.and]: [
{ designation: { [Op.iLike]: 'dealer' } as any },
{
[Op.or]: [
{ displayName: { [Op.iLike]: `%${searchTerm}%` } as any },
{ email: { [Op.iLike]: `%${searchTerm}%` } as any },
{ employeeId: { [Op.iLike]: `%${searchTerm}%` } as any },
],
},
{ isActive: true },
],
},
order: [['displayName', 'ASC']],
limit: 50, // Limit results
});
return dealers.map((dealer) => ({
userId: dealer.userId,
email: dealer.email,
dealerCode: dealer.employeeId || '',
dealerName: dealer.displayName || dealer.email,
displayName: dealer.displayName || dealer.email,
phone: dealer.phone || undefined,
department: dealer.department || undefined,
designation: dealer.designation || undefined,
}));
return await getAllDealers(searchTerm, limit);
} catch (error) {
logger.error('[DealerService] Error searching dealers:', error);
throw error;