flow created upto outlet creation
This commit is contained in:
parent
c6946eae4e
commit
6dc506077f
63
scripts/migrate_user_columns.ts
Normal file
63
scripts/migrate_user_columns.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { Sequelize } from 'sequelize';
|
||||||
|
import config from '../src/common/config/database.js';
|
||||||
|
|
||||||
|
const env = process.env.NODE_ENV || 'development';
|
||||||
|
const dbConfig = config[env];
|
||||||
|
|
||||||
|
const sequelize = new Sequelize(
|
||||||
|
dbConfig.database,
|
||||||
|
dbConfig.username,
|
||||||
|
dbConfig.password,
|
||||||
|
{
|
||||||
|
host: dbConfig.host,
|
||||||
|
port: dbConfig.port,
|
||||||
|
dialect: dbConfig.dialect,
|
||||||
|
logging: console.log
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
async function migrate() {
|
||||||
|
try {
|
||||||
|
await sequelize.authenticate();
|
||||||
|
console.log('Connected to database.');
|
||||||
|
|
||||||
|
const queryInterface = sequelize.getQueryInterface();
|
||||||
|
|
||||||
|
// Check if users table exists
|
||||||
|
const tables = await queryInterface.showAllTables();
|
||||||
|
if (!tables.includes('users')) {
|
||||||
|
console.log('Users table does not exist. Skipping rename.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename fullName to name
|
||||||
|
const columns = await queryInterface.describeTable('users');
|
||||||
|
|
||||||
|
if (columns.fullName && !columns.name) {
|
||||||
|
console.log('Renaming fullName to name...');
|
||||||
|
await queryInterface.renameColumn('users', 'fullName', 'name');
|
||||||
|
} else if (columns.fullName && columns.name) {
|
||||||
|
console.log('Both fullName and name exist. Manual intervention needed.');
|
||||||
|
} else {
|
||||||
|
console.log('fullName not found or name already exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename mobileNumber to phone
|
||||||
|
if (columns.mobileNumber && !columns.phone) {
|
||||||
|
console.log('Renaming mobileNumber to phone...');
|
||||||
|
await queryInterface.renameColumn('users', 'mobileNumber', 'phone');
|
||||||
|
} else if (columns.mobileNumber && columns.phone) {
|
||||||
|
console.log('Both mobileNumber and phone exist. Manual intervention needed.');
|
||||||
|
} else {
|
||||||
|
console.log('mobileNumber not found or phone already exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Migration successful.');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Migration failed:', error);
|
||||||
|
} finally {
|
||||||
|
await sequelize.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate();
|
||||||
@ -289,6 +289,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
scope: { requestType: 'application' }
|
scope: { requestType: 'application' }
|
||||||
});
|
});
|
||||||
Application.hasOne(models.DealerCode, { foreignKey: 'applicationId', as: 'dealerCode' });
|
Application.hasOne(models.DealerCode, { foreignKey: 'applicationId', as: 'dealerCode' });
|
||||||
|
Application.hasOne(models.Dealer, { foreignKey: 'applicationId', as: 'dealer' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return Application;
|
return Application;
|
||||||
|
|||||||
@ -83,6 +83,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
Dealer.hasMany(models.Document, { foreignKey: 'dealerId', as: 'documents' });
|
Dealer.hasMany(models.Document, { foreignKey: 'dealerId', as: 'documents' });
|
||||||
Dealer.hasMany(models.Resignation, { foreignKey: 'dealerId', as: 'resignations' });
|
Dealer.hasMany(models.Resignation, { foreignKey: 'dealerId', as: 'resignations' });
|
||||||
Dealer.hasMany(models.TerminationRequest, { foreignKey: 'dealerId', as: 'terminationRequests' });
|
Dealer.hasMany(models.TerminationRequest, { foreignKey: 'dealerId', as: 'terminationRequests' });
|
||||||
|
Dealer.hasOne(models.User, { foreignKey: 'dealerId', as: 'user' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return Dealer;
|
return Dealer;
|
||||||
|
|||||||
@ -160,6 +160,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
User.hasMany(models.AuditLog, { foreignKey: 'userId', as: 'auditLogs' });
|
User.hasMany(models.AuditLog, { foreignKey: 'userId', as: 'auditLogs' });
|
||||||
User.hasMany(models.AreaManager, { foreignKey: 'userId', as: 'areaManagers' });
|
User.hasMany(models.AreaManager, { foreignKey: 'userId', as: 'areaManagers' });
|
||||||
|
User.belongsTo(models.Dealer, { foreignKey: 'dealerId', as: 'dealerProfile' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return User;
|
return User;
|
||||||
|
|||||||
@ -309,7 +309,7 @@ export const addParticipant = async (req: AuthRequest, res: Response) => {
|
|||||||
user.email,
|
user.email,
|
||||||
user.fullName,
|
user.fullName,
|
||||||
application.applicationId || application.id,
|
application.applicationId || application.id,
|
||||||
application.name, // The dealer's name
|
application.applicantName, // The dealer's name
|
||||||
participantType || 'participant'
|
participantType || 'participant'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
import db from '../../database/models/index.js';
|
import db from '../../database/models/index.js';
|
||||||
const { Dealer, DealerCode, Application, User, AuditLog } = db;
|
const {
|
||||||
|
Dealer, DealerCode, Application, User, AuditLog, Outlet, Region, Zone,
|
||||||
|
Resignation, RelocationRequest, ConstitutionalChange
|
||||||
|
} = db;
|
||||||
import { AuthRequest } from '../../types/express.types.js';
|
import { AuthRequest } from '../../types/express.types.js';
|
||||||
import { AUDIT_ACTIONS } from '../../common/config/constants.js';
|
import { AUDIT_ACTIONS } from '../../common/config/constants.js';
|
||||||
|
|
||||||
@ -27,27 +31,160 @@ export const createDealer = async (req: AuthRequest, res: Response) => {
|
|||||||
const application = await Application.findByPk(applicationId);
|
const application = await Application.findByPk(applicationId);
|
||||||
if (!application) return res.status(404).json({ success: false, message: 'Application not found' });
|
if (!application) return res.status(404).json({ success: false, message: 'Application not found' });
|
||||||
|
|
||||||
// Mark Code as Used
|
// Find existing dealer or auto-detect dealer code
|
||||||
if (dealerCodeId) {
|
let targetDealerCodeId = dealerCodeId;
|
||||||
await DealerCode.update({ isUsed: true, usedAt: new Date(), usedByApplicationId: applicationId }, { where: { id: dealerCodeId } });
|
if (!targetDealerCodeId) {
|
||||||
|
const dealerCodeRecord = await DealerCode.findOne({ where: { applicationId } });
|
||||||
|
if (dealerCodeRecord) {
|
||||||
|
targetDealerCodeId = dealerCodeRecord.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dealer = await Dealer.create({
|
const existingDealer = await Dealer.findOne({ where: { applicationId } });
|
||||||
applicationId,
|
let dealer = existingDealer;
|
||||||
dealerCodeId,
|
|
||||||
dealerName: application.applicantName, // Or Trade Name
|
if (existingDealer) {
|
||||||
status: 'Active',
|
// Update existing dealer if it's missing the code
|
||||||
onboardedAt: new Date()
|
if (!existingDealer.dealerCodeId && targetDealerCodeId) {
|
||||||
});
|
await existingDealer.update({ dealerCodeId: targetDealerCodeId });
|
||||||
|
|
||||||
|
// Mark Code as Used
|
||||||
|
await DealerCode.update({
|
||||||
|
isUsed: true,
|
||||||
|
usedAt: new Date(),
|
||||||
|
usedByApplicationId: applicationId
|
||||||
|
}, { where: { id: targetDealerCodeId } });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Mark Code as Used
|
||||||
|
if (targetDealerCodeId) {
|
||||||
|
await DealerCode.update({
|
||||||
|
isUsed: true,
|
||||||
|
usedAt: new Date(),
|
||||||
|
usedByApplicationId: applicationId
|
||||||
|
}, { where: { id: targetDealerCodeId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Dealer Profile
|
||||||
|
dealer = await Dealer.create({
|
||||||
|
applicationId,
|
||||||
|
dealerCodeId: targetDealerCodeId,
|
||||||
|
legalName: application.applicantName,
|
||||||
|
businessName: application.applicantName,
|
||||||
|
constitutionType: application.businessType,
|
||||||
|
status: 'Active',
|
||||||
|
onboardedAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
await AuditLog.create({
|
||||||
|
userId: req.user?.id,
|
||||||
|
action: AUDIT_ACTIONS.CREATED,
|
||||||
|
entityType: 'dealer',
|
||||||
|
entityId: dealer.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Create or Update User Account for the Dealer ---
|
||||||
|
let user = await User.findOne({ where: { email: application.email } });
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
// Update existing user to Dealer role and link dealerId
|
||||||
|
await user.update({
|
||||||
|
roleCode: 'Dealer',
|
||||||
|
dealerId: dealer.id,
|
||||||
|
status: 'active',
|
||||||
|
isActive: true
|
||||||
|
});
|
||||||
|
console.log(`[Dealer Onboarding] Updated existing user ${user.email} to Dealer role.`);
|
||||||
|
} else {
|
||||||
|
// Create new User account
|
||||||
|
const hashedPassword = await bcrypt.hash('Dealer@123', 10); // Default password
|
||||||
|
user = await User.create({
|
||||||
|
fullName: application.applicantName,
|
||||||
|
email: application.email,
|
||||||
|
password: hashedPassword,
|
||||||
|
roleCode: 'Dealer',
|
||||||
|
dealerId: dealer.id,
|
||||||
|
mobileNumber: application.phone,
|
||||||
|
status: 'active',
|
||||||
|
isActive: true,
|
||||||
|
isExternal: true, // Dealers are external users
|
||||||
|
zoneId: application.zoneId,
|
||||||
|
regionId: application.regionId
|
||||||
|
});
|
||||||
|
console.log(`[Dealer Onboarding] Created new Dealer user account for ${user.email}.`);
|
||||||
|
}
|
||||||
|
|
||||||
await AuditLog.create({
|
await AuditLog.create({
|
||||||
userId: req.user?.id,
|
userId: req.user?.id,
|
||||||
action: AUDIT_ACTIONS.CREATED,
|
action: AUDIT_ACTIONS.CREATED,
|
||||||
entityType: 'dealer',
|
entityType: 'user',
|
||||||
entityId: dealer.id
|
entityId: user.id,
|
||||||
|
newData: { roleCode: 'Dealer', dealerId: dealer.id }
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json({ success: true, message: 'Dealer profile created', data: dealer });
|
// --- Create Primary Outlet for the Dealer ---
|
||||||
|
// Check if outlet already exists
|
||||||
|
let outlet = await Outlet.findOne({ where: { dealerId: user.id } });
|
||||||
|
if (!outlet) {
|
||||||
|
let regionName = 'Central';
|
||||||
|
let zoneName = 'National';
|
||||||
|
|
||||||
|
if (application.regionId) {
|
||||||
|
const reg = await Region.findByPk(application.regionId);
|
||||||
|
if (reg) regionName = reg.regionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (application.zoneId) {
|
||||||
|
const zon = await Zone.findByPk(application.zoneId);
|
||||||
|
if (zon) zoneName = zon.zoneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map region name to valid ENUM values if necessary
|
||||||
|
const validRegions = ['East', 'West', 'North', 'South', 'Central'];
|
||||||
|
if (!validRegions.includes(regionName)) {
|
||||||
|
regionName = 'Central'; // Fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
const dealerCodeRecord = await DealerCode.findOne({ where: { applicationId: application.id } });
|
||||||
|
const outletCode = `OUT-${dealerCodeRecord?.dealerCode || Date.now().toString().slice(-6)}`;
|
||||||
|
|
||||||
|
outlet = await Outlet.create({
|
||||||
|
code: outletCode,
|
||||||
|
name: `${application.applicantName} - ${application.city || 'Primary'}`,
|
||||||
|
type: application.businessType === 'Studio' ? 'Studio' : 'Dealership',
|
||||||
|
address: application.address || application.businessAddress || 'Address Pending',
|
||||||
|
city: application.city || 'City Pending',
|
||||||
|
state: application.state || 'State Pending',
|
||||||
|
pincode: application.pincode || '000000',
|
||||||
|
status: 'Active',
|
||||||
|
establishedDate: new Date(),
|
||||||
|
dealerId: user.id,
|
||||||
|
region: regionName as any,
|
||||||
|
zone: zoneName
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[Dealer Onboarding] Created primary outlet ${outlet.code} for dealer ${user.email}.`);
|
||||||
|
|
||||||
|
await AuditLog.create({
|
||||||
|
userId: req.user?.id,
|
||||||
|
action: AUDIT_ACTIONS.CREATED,
|
||||||
|
entityType: 'outlet',
|
||||||
|
entityId: outlet.id,
|
||||||
|
newData: { code: outlet.code, name: outlet.name }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
message: 'Dealer profile, user account, and primary outlet created successfully',
|
||||||
|
data: {
|
||||||
|
dealer,
|
||||||
|
user: { email: user.email, role: user.roleCode },
|
||||||
|
outlet: { code: outlet.code, name: outlet.name }
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Create dealer error:', error);
|
console.error('Create dealer error:', error);
|
||||||
res.status(500).json({ success: false, message: 'Error creating dealer' });
|
res.status(500).json({ success: false, message: 'Error creating dealer' });
|
||||||
@ -69,3 +206,68 @@ export const updateDealer = async (req: AuthRequest, res: Response) => {
|
|||||||
res.status(500).json({ success: false, message: 'Error updating dealer' });
|
res.status(500).json({ success: false, message: 'Error updating dealer' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDealerDashboard = async (req: AuthRequest, res: Response) => {
|
||||||
|
try {
|
||||||
|
const userId = req.user?.id;
|
||||||
|
if (!userId) return res.status(401).json({ success: false, message: 'Unauthorized' });
|
||||||
|
|
||||||
|
// 1. Fetch User and linked Dealer Profile
|
||||||
|
const user = await User.findByPk(userId, {
|
||||||
|
include: [{
|
||||||
|
association: 'dealerProfile',
|
||||||
|
include: [{ association: 'dealerCode' }]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Fetch Active Outlets
|
||||||
|
const outlets = await Outlet.findAll({
|
||||||
|
where: { dealerId: userId },
|
||||||
|
order: [['createdAt', 'DESC']]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Fetch Request Counts & Recent History
|
||||||
|
const [resignations, relocations, constitutional] = await Promise.all([
|
||||||
|
Resignation.findAll({ where: { dealerId: userId }, order: [['createdAt', 'DESC']] }),
|
||||||
|
RelocationRequest.findAll({ where: { dealerId: userId }, order: [['createdAt', 'DESC']] }),
|
||||||
|
ConstitutionalChange.findAll({ where: { dealerId: userId }, order: [['createdAt', 'DESC']] })
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Combine and sort recent requests
|
||||||
|
const allRequests = [
|
||||||
|
...resignations.map((r: any) => ({ id: r.resignationId, type: 'Resignation', title: r.reason, status: r.status, date: r.submittedOn || (r as any).createdAt, color: 'bg-red-100 text-red-700 border-red-300' })),
|
||||||
|
...relocations.map((r: any) => ({ id: r.requestId, type: 'Relocation', title: `Relocation to ${r.newCity}`, status: r.status, date: (r as any).createdAt, color: 'bg-amber-100 text-amber-700 border-amber-300' })),
|
||||||
|
...constitutional.map((r: any) => ({ id: r.requestId, type: 'Constitutional Change', title: r.changeType, status: r.status, date: (r as any).createdAt, color: 'bg-blue-100 text-blue-700 border-blue-300' }))
|
||||||
|
].sort((a: any, b: any) => new Date(b.date).getTime() - new Date(a.date).getTime()).slice(0, 5);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
profile: {
|
||||||
|
name: user?.fullName,
|
||||||
|
email: user?.email,
|
||||||
|
dealerCode: (user as any)?.dealerProfile?.dealerCode?.code || 'N/A',
|
||||||
|
businessName: (user as any)?.dealerProfile?.businessName || 'N/A'
|
||||||
|
},
|
||||||
|
outlets: outlets.map((o: any) => ({
|
||||||
|
id: o.id,
|
||||||
|
name: o.name,
|
||||||
|
code: o.code,
|
||||||
|
type: o.type,
|
||||||
|
status: o.status,
|
||||||
|
location: `${o.city}, ${o.state}`
|
||||||
|
})),
|
||||||
|
stats: {
|
||||||
|
constitutional: constitutional.length,
|
||||||
|
relocation: relocations.length,
|
||||||
|
resignation: resignations.length,
|
||||||
|
total: constitutional.length + relocations.length + resignations.length
|
||||||
|
},
|
||||||
|
recentRequests: allRequests
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Get dealer dashboard error:', error);
|
||||||
|
res.status(500).json({ success: false, message: 'Error fetching dashboard data' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { authenticate } from '../../common/middleware/auth.js';
|
|||||||
|
|
||||||
router.use(authenticate as any);
|
router.use(authenticate as any);
|
||||||
|
|
||||||
|
router.get('/dashboard', dealerController.getDealerDashboard);
|
||||||
router.get('/', dealerController.getDealers);
|
router.get('/', dealerController.getDealers);
|
||||||
router.post('/', dealerController.createDealer);
|
router.post('/', dealerController.createDealer);
|
||||||
router.put('/:id', dealerController.updateDealer);
|
router.put('/:id', dealerController.updateDealer);
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export const getOutlets = async (req: AuthRequest, res: Response) => {
|
|||||||
{
|
{
|
||||||
model: User,
|
model: User,
|
||||||
as: 'dealer',
|
as: 'dealer',
|
||||||
attributes: ['name', 'email']
|
attributes: ['fullName', 'email']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: Resignation,
|
model: Resignation,
|
||||||
@ -54,7 +54,7 @@ export const getOutletById = async (req: AuthRequest, res: Response) => {
|
|||||||
include: [{
|
include: [{
|
||||||
model: User,
|
model: User,
|
||||||
as: 'dealer',
|
as: 'dealer',
|
||||||
attributes: ['name', 'email', 'phone']
|
attributes: ['fullName', 'email', 'mobileNumber']
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ export const getOutletByCode = async (req: AuthRequest, res: Response) => {
|
|||||||
include: [{
|
include: [{
|
||||||
model: User,
|
model: User,
|
||||||
as: 'dealer',
|
as: 'dealer',
|
||||||
attributes: ['name', 'email', 'phone']
|
attributes: ['fullName', 'email', 'mobileNumber']
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -190,12 +190,9 @@ export const getApplicationById = async (req: AuthRequest, res: Response) => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{ model: db.RequestParticipant, as: 'participants', include: [{ model: db.User, as: 'user', attributes: ['id', ['fullName', 'name'], 'email', ['roleCode', 'role']] }] },
|
||||||
model: db.RequestParticipant,
|
{ model: db.DealerCode, as: 'dealerCode' },
|
||||||
as: 'participants',
|
{ model: db.Dealer, as: 'dealer' }
|
||||||
include: [{ model: db.User, as: 'user', attributes: ['id', ['fullName', 'name'], 'email', ['roleCode', 'role']] }]
|
|
||||||
},
|
|
||||||
{ model: db.DealerCode, as: 'dealerCode' }
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -158,7 +158,7 @@ export const getResignations = async (req: AuthRequest, res: Response, next: Nex
|
|||||||
{
|
{
|
||||||
model: db.User,
|
model: db.User,
|
||||||
as: 'dealer',
|
as: 'dealer',
|
||||||
attributes: ['id', 'name', 'email', 'phone']
|
attributes: ['id', 'fullName', 'email', 'mobileNumber']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
order: [['createdAt', 'DESC']],
|
order: [['createdAt', 'DESC']],
|
||||||
@ -199,7 +199,7 @@ export const getResignationById = async (req: AuthRequest, res: Response, next:
|
|||||||
{
|
{
|
||||||
model: db.User,
|
model: db.User,
|
||||||
as: 'dealer',
|
as: 'dealer',
|
||||||
attributes: ['id', 'name', 'email', 'phone']
|
attributes: ['id', 'fullName', 'email', 'mobileNumber']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const getFnFSettlements = async (req: Request, res: Response) => {
|
|||||||
include: [{
|
include: [{
|
||||||
model: User,
|
model: User,
|
||||||
as: 'dealer',
|
as: 'dealer',
|
||||||
attributes: ['name', 'id']
|
attributes: ['fullName', 'id']
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
69
src/scripts/fix_missing_outlets.ts
Normal file
69
src/scripts/fix_missing_outlets.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import db from '../database/models/index.js';
|
||||||
|
const { User, Dealer, Outlet, Application, DealerCode } = db;
|
||||||
|
|
||||||
|
async function fixMissingOutlets() {
|
||||||
|
try {
|
||||||
|
console.log('--- Starting Missing Outlet Fix ---');
|
||||||
|
|
||||||
|
// 1. Find all users with role 'Dealer'
|
||||||
|
const dealerUsers = await User.findAll({
|
||||||
|
where: { roleCode: 'Dealer' }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Found ${dealerUsers.length} dealer users.`);
|
||||||
|
|
||||||
|
for (const user of dealerUsers) {
|
||||||
|
// 2. Check if this dealer already has an outlet
|
||||||
|
const existingOutlet = await Outlet.findOne({ where: { dealerId: user.id } });
|
||||||
|
|
||||||
|
if (existingOutlet) {
|
||||||
|
console.log(`Dealer ${user.email} already has outlet: ${existingOutlet.code}. Skipping.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Dealer ${user.email} is missing an outlet. Attempting to create one...`);
|
||||||
|
|
||||||
|
// 3. Find the dealer profile to get the application
|
||||||
|
const dealerProfile = await Dealer.findOne({ where: { id: user.dealerId } });
|
||||||
|
if (!dealerProfile) {
|
||||||
|
console.warn(`Could not find dealer profile for user ${user.email}. Skipping.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const application = await Application.findByPk(dealerProfile.applicationId);
|
||||||
|
if (!application) {
|
||||||
|
console.warn(`Could not find application for dealer ${user.email}. Skipping.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Create the outlet (same logic as in dealer.controller.ts)
|
||||||
|
const dealerCodeRecord = await DealerCode.findOne({ where: { applicationId: application.id } });
|
||||||
|
const outletCode = `OUT-${dealerCodeRecord ? (dealerCodeRecord.dealerCode || dealerCodeRecord.code) : Date.now().toString().slice(-6)}`;
|
||||||
|
|
||||||
|
const outlet = await Outlet.create({
|
||||||
|
code: outletCode,
|
||||||
|
name: `${application.applicantName} - ${application.city || 'Primary'}`,
|
||||||
|
type: application.businessType === 'Studio' ? 'Studio' : 'Dealership',
|
||||||
|
address: application.address || application.businessAddress || 'Address Pending',
|
||||||
|
city: application.city || 'City Pending',
|
||||||
|
state: application.state || 'State Pending',
|
||||||
|
pincode: application.pincode || '000000',
|
||||||
|
status: 'Active',
|
||||||
|
establishedDate: new Date(),
|
||||||
|
dealerId: user.id,
|
||||||
|
region: 'Central', // Default fallback
|
||||||
|
zone: 'National'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Successfully created outlet ${outlet.code} for dealer ${user.email}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('--- Fix Completed ---');
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fixing missing outlets:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fixMissingOutlets();
|
||||||
@ -128,19 +128,30 @@ app.use('/api/questionnaire', questionnaireRoutes);
|
|||||||
app.use('/api/prospective-login', prospectiveLoginRoutes);
|
app.use('/api/prospective-login', prospectiveLoginRoutes);
|
||||||
app.use('/api/termination', terminationRoutes);
|
app.use('/api/termination', terminationRoutes);
|
||||||
|
|
||||||
// Backward Compatibility Aliases
|
// Backward Compatibility & Frontend Mapping Aliases
|
||||||
app.use('/api/applications', onboardingRoutes);
|
app.use('/api/applications', onboardingRoutes);
|
||||||
app.use('/api/resignations', resignationRoutes);
|
app.use('/api/resignations', resignationRoutes);
|
||||||
|
app.use('/api/resignation', resignationRoutes); // Singular alias
|
||||||
app.use('/api/constitutional', (req: Request, res: Response, next: NextFunction) => {
|
app.use('/api/constitutional', (req: Request, res: Response, next: NextFunction) => {
|
||||||
// Map /api/constitutional to /api/self-service/constitutional
|
// Map /api/constitutional to /api/self-service/constitutional
|
||||||
req.url = '/constitutional' + req.url;
|
req.url = '/constitutional' + req.url;
|
||||||
next();
|
next();
|
||||||
}, selfServiceRoutes);
|
}, selfServiceRoutes);
|
||||||
|
app.use('/api/constitutional-change', (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
// Alias for constitutional-change
|
||||||
|
req.url = '/constitutional' + req.url;
|
||||||
|
next();
|
||||||
|
}, selfServiceRoutes);
|
||||||
app.use('/api/relocations', (req: Request, res: Response, next: NextFunction) => {
|
app.use('/api/relocations', (req: Request, res: Response, next: NextFunction) => {
|
||||||
// Map /api/relocations to /api/self-service/relocation
|
// Map /api/relocations to /api/self-service/relocation
|
||||||
req.url = '/relocation' + req.url;
|
req.url = '/relocation' + req.url;
|
||||||
next();
|
next();
|
||||||
}, selfServiceRoutes);
|
}, selfServiceRoutes);
|
||||||
|
app.use('/api/relocation', (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
// Alias for relocation
|
||||||
|
req.url = '/relocation' + req.url;
|
||||||
|
next();
|
||||||
|
}, selfServiceRoutes);
|
||||||
app.use('/api/outlets', outletRoutes);
|
app.use('/api/outlets', outletRoutes);
|
||||||
app.use('/api/finance', settlementRoutes);
|
app.use('/api/finance', settlementRoutes);
|
||||||
app.use('/api/worknotes', collaborationRoutes);
|
app.use('/api/worknotes', collaborationRoutes);
|
||||||
@ -167,7 +178,7 @@ const startServer = async () => {
|
|||||||
|
|
||||||
// Sync database (in development only)
|
// Sync database (in development only)
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
await db.sequelize.sync({ alter: true });
|
await db.sequelize.sync({ alter: false });
|
||||||
logger.info('Database models synchronized');
|
logger.info('Database models synchronized');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user