started progress bar end to end flowe with mockup at backend for external dependecies
This commit is contained in:
parent
c43f86253b
commit
c6946eae4e
18
scripts/check_column.ts
Normal file
18
scripts/check_column.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import db from '../src/database/models/index.js';
|
||||
|
||||
async function checkColumn() {
|
||||
try {
|
||||
const [results]: any = await db.sequelize.query(`
|
||||
SELECT column_name, data_type, udt_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'request_participants' AND column_name = 'participantType'
|
||||
`);
|
||||
console.log('Column definition:', results[0]);
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching column:', error.message);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
checkColumn();
|
||||
19
scripts/check_enum.ts
Normal file
19
scripts/check_enum.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import db from '../src/database/models/index.js';
|
||||
|
||||
async function checkEnum() {
|
||||
try {
|
||||
const [results]: any = await db.sequelize.query(`
|
||||
SELECT enumlabel
|
||||
FROM pg_enum
|
||||
JOIN pg_type ON pg_enum.enumtypid = pg_type.oid
|
||||
WHERE typname = 'enum_request_participants_participantType'
|
||||
`);
|
||||
console.log('Current enum values:', results.map((r: any) => r.enumlabel).join(', '));
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching enum:', error.message);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
checkEnum();
|
||||
26
scripts/test_enum_cast.ts
Normal file
26
scripts/test_enum_cast.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import db from '../src/database/models/index.js';
|
||||
|
||||
async function testInsert() {
|
||||
try {
|
||||
// Attempt insert without checking existing records
|
||||
// If it fails with "invalid input value", the enum is truly not updated.
|
||||
// If it fails with "foreign key", the enum was VALID but the data was wrong.
|
||||
await db.sequelize.query(`
|
||||
DO $$
|
||||
BEGIN
|
||||
-- This will fail if 'architecture' is invalid for the enum
|
||||
PERFORM 'architecture'::"enum_request_participants_participantType";
|
||||
RAISE NOTICE '✅ Enum check passed!';
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE EXCEPTION '❌ Enum check failed: %', SQLERRM;
|
||||
END $$;
|
||||
`);
|
||||
console.log('✅ PL/pgSQL Enum check passed!');
|
||||
} catch (error: any) {
|
||||
console.error(error.message);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
testInsert();
|
||||
22
scripts/test_insert.ts
Normal file
22
scripts/test_insert.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import db from '../src/database/models/index.js';
|
||||
|
||||
async function testInsert() {
|
||||
try {
|
||||
const testId = '00000000-0000-0000-0000-000000000000';
|
||||
await db.sequelize.query(`
|
||||
INSERT INTO request_participants ("id", "requestId", "requestType", "userId", "participantType", "joinedMethod", "createdAt", "updatedAt")
|
||||
VALUES (gen_random_uuid(), '${testId}', 'test', '9950ee60-ddf6-4091-a1e6-e7161e6d8bb6', 'architecture', 'manual', now(), now())
|
||||
`);
|
||||
console.log('✅ Manual insert successful!');
|
||||
|
||||
// Clean up
|
||||
await db.sequelize.query(`DELETE FROM request_participants WHERE "requestId" = '${testId}'`);
|
||||
console.log('✅ Clean up successful!');
|
||||
} catch (error: any) {
|
||||
console.error('❌ Manual insert failed:', error.message);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
testInsert();
|
||||
46
scripts/update_dealer_codes_table.ts
Normal file
46
scripts/update_dealer_codes_table.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import db from '../src/database/models/index.js';
|
||||
const { sequelize } = db;
|
||||
|
||||
async function updateDealerCodesTable() {
|
||||
console.log('🔄 Checking and updating dealer_codes table schema...');
|
||||
|
||||
try {
|
||||
// Add applicationId
|
||||
await sequelize.query(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='dealer_codes' AND column_name='applicationId') THEN
|
||||
ALTER TABLE dealer_codes ADD COLUMN "applicationId" UUID REFERENCES applications(id);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='dealer_codes' AND column_name='salesCode') THEN
|
||||
ALTER TABLE dealer_codes ADD COLUMN "salesCode" VARCHAR(255);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='dealer_codes' AND column_name='serviceCode') THEN
|
||||
ALTER TABLE dealer_codes ADD COLUMN "serviceCode" VARCHAR(255);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='dealer_codes' AND column_name='gmaCode') THEN
|
||||
ALTER TABLE dealer_codes ADD COLUMN "gmaCode" VARCHAR(255);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='dealer_codes' AND column_name='gearCode') THEN
|
||||
ALTER TABLE dealer_codes ADD COLUMN "gearCode" VARCHAR(255);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='dealer_codes' AND column_name='sapMasterId') THEN
|
||||
ALTER TABLE dealer_codes ADD COLUMN "sapMasterId" VARCHAR(255);
|
||||
END IF;
|
||||
END $$;
|
||||
`);
|
||||
|
||||
console.log('✅ dealer_codes table schema updated successfully.');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ Error updating dealer_codes table:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
updateDealerCodesTable();
|
||||
33
scripts/update_enum.ts
Normal file
33
scripts/update_enum.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { APPLICATION_STATUS } from '../src/common/config/constants.js';
|
||||
import db from '../src/database/models/index.js';
|
||||
|
||||
async function updateEnum() {
|
||||
try {
|
||||
console.log('🔄 Syncing all APPLICATION_STATUS values with DB Enum...');
|
||||
|
||||
const statuses = Object.values(APPLICATION_STATUS);
|
||||
|
||||
for (const status of statuses) {
|
||||
try {
|
||||
// Posgres doesn't support IF NOT EXISTS for ADD VALUE in 9.5 and below
|
||||
// so we do it one by one and ignore "already exists" errors.
|
||||
await db.sequelize.query(`ALTER TYPE "enum_applications_overallStatus" ADD VALUE '${status}'`);
|
||||
console.log(`✅ Added: ${status}`);
|
||||
} catch (e: any) {
|
||||
if (e.message.includes('already exists')) {
|
||||
// console.log(`ℹ️ Already exists: ${status}`);
|
||||
} else {
|
||||
console.error(`❌ Error adding ${status}:`, e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Database Enum successfully synchronized with constants.');
|
||||
} catch (error: any) {
|
||||
console.error('❌ Critical failure during sync:', error.message);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
updateEnum();
|
||||
26
scripts/update_participant_enum.ts
Normal file
26
scripts/update_participant_enum.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import db from '../src/database/models/index.js';
|
||||
|
||||
async function updateParticipantEnum() {
|
||||
try {
|
||||
console.log('🔄 Adding "architecture" to participantType enum...');
|
||||
|
||||
try {
|
||||
await db.sequelize.query(`ALTER TYPE "enum_request_participants_participantType" ADD VALUE 'architecture'`);
|
||||
console.log(`✅ Added: architecture`);
|
||||
} catch (e: any) {
|
||||
if (e.message.includes('already exists')) {
|
||||
console.log(`ℹ️ Already exists: architecture`);
|
||||
} else {
|
||||
console.error(`❌ Error adding architecture:`, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Database Enum successfully updated.');
|
||||
} catch (error: any) {
|
||||
console.error('❌ Critical failure:', error.message);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
updateParticipantEnum();
|
||||
@ -63,6 +63,7 @@ export const APPLICATION_STATUS = {
|
||||
LEVEL_3_APPROVED: 'Level 3 Approved',
|
||||
FDD_VERIFICATION: 'FDD Verification',
|
||||
PAYMENT_PENDING: 'Payment Pending',
|
||||
LOI_IN_PROGRESS: 'LOI In Progress',
|
||||
LOI_ISSUED: 'LOI Issued',
|
||||
DEALER_CODE_GENERATION: 'Dealer Code Generation',
|
||||
ARCHITECTURE_TEAM_ASSIGNED: 'Architecture Team Assigned',
|
||||
@ -304,6 +305,18 @@ export const DOCUMENT_TYPES = {
|
||||
BOARD_RESOLUTION: 'Board Resolution',
|
||||
PROPERTY_DOCUMENTS: 'Property Documents',
|
||||
BANK_STATEMENT: 'Bank Statement',
|
||||
NODAL_AGREEMENT: 'Nodal Agreement',
|
||||
CANCELLED_CHECK: 'Cancelled Check',
|
||||
FIRM_REGISTRATION: 'Firm Registration',
|
||||
RENTAL_AGREEMENT: 'Rental Agreement',
|
||||
VIRTUAL_CODE: 'Virtual Code Confirmation',
|
||||
DOMAIN_ID: 'Domain ID Setup',
|
||||
MSD_CONFIG: 'MSD Configuration',
|
||||
LOI_ACK: 'LOI Acknowledgement',
|
||||
ARCHITECTURE_ASSIGNMENT: 'Architecture Assignment Document',
|
||||
ARCHITECTURE_BLUEPRINT: 'Architecture Blueprint',
|
||||
SITE_PLAN: 'Site Plan',
|
||||
ARCHITECTURE_COMPLETION: 'Architecture Completion Certificate',
|
||||
OTHER: 'Other'
|
||||
} as const;
|
||||
|
||||
|
||||
@ -37,6 +37,9 @@ export interface ApplicationAttributes {
|
||||
zoneId: string | null;
|
||||
regionId: string | null;
|
||||
areaId: string | null;
|
||||
architectureAssignedDate: Date | null;
|
||||
architectureDocumentDate: Date | null;
|
||||
architectureCompletionDate: Date | null;
|
||||
score: number;
|
||||
documents: any[];
|
||||
timeline: any[];
|
||||
@ -223,6 +226,18 @@ export default (sequelize: Sequelize) => {
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
architectureAssignedDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true
|
||||
},
|
||||
architectureDocumentDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true
|
||||
},
|
||||
architectureCompletionDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true
|
||||
},
|
||||
documents: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: []
|
||||
@ -273,6 +288,7 @@ export default (sequelize: Sequelize) => {
|
||||
as: 'participants',
|
||||
scope: { requestType: 'application' }
|
||||
});
|
||||
Application.hasOne(models.DealerCode, { foreignKey: 'applicationId', as: 'dealerCode' });
|
||||
};
|
||||
|
||||
return Application;
|
||||
|
||||
@ -3,6 +3,12 @@ import { Model, DataTypes, Sequelize } from 'sequelize';
|
||||
export interface DealerCodeAttribute {
|
||||
id: string;
|
||||
dealerCode: string;
|
||||
applicationId: string | null;
|
||||
salesCode: string | null;
|
||||
serviceCode: string | null;
|
||||
gmaCode: string | null;
|
||||
gearCode: string | null;
|
||||
sapMasterId: string | null;
|
||||
status: string;
|
||||
generatedAt: Date;
|
||||
generatedBy: string | null;
|
||||
@ -22,6 +28,34 @@ export default (sequelize: Sequelize) => {
|
||||
unique: true,
|
||||
allowNull: false
|
||||
},
|
||||
applicationId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true,
|
||||
references: {
|
||||
model: 'applications',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
salesCode: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
serviceCode: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
gmaCode: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
gearCode: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
sapMasterId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'active'
|
||||
@ -47,6 +81,7 @@ export default (sequelize: Sequelize) => {
|
||||
(DealerCode as any).associate = (models: any) => {
|
||||
DealerCode.belongsTo(models.User, { foreignKey: 'generatedBy', as: 'generator' });
|
||||
DealerCode.hasOne(models.Dealer, { foreignKey: 'dealerCodeId', as: 'dealer' });
|
||||
DealerCode.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
||||
};
|
||||
|
||||
return DealerCode;
|
||||
|
||||
@ -36,7 +36,7 @@ export default (sequelize: Sequelize) => {
|
||||
}
|
||||
},
|
||||
participantType: {
|
||||
type: DataTypes.ENUM('owner', 'assignee', 'reviewer', 'contributor', 'observer'),
|
||||
type: DataTypes.ENUM('owner', 'assignee', 'reviewer', 'contributor', 'observer', 'architecture'),
|
||||
defaultValue: 'contributor'
|
||||
},
|
||||
joinedMethod: {
|
||||
|
||||
@ -163,15 +163,26 @@ export const createUser = async (req: AuthRequest, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
const existingUser = await User.findOne({ where: { email } });
|
||||
if (existingUser) {
|
||||
// Check if user already exists (Email)
|
||||
const existingEmail = await User.findOne({ where: { email } });
|
||||
if (existingEmail) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'User with this email already exists'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user already exists (Employee ID)
|
||||
if (employeeId) {
|
||||
const existingEmpId = await User.findOne({ where: { employeeId } });
|
||||
if (existingEmpId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `User with Employee ID ${employeeId} already exists`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Hash default password
|
||||
const hashedPassword = await bcrypt.hash('Admin@123', 10);
|
||||
|
||||
@ -203,8 +214,16 @@ export const createUser = async (req: AuthRequest, res: Response) => {
|
||||
});
|
||||
|
||||
res.status(201).json({ success: true, message: 'User created successfully', data: user });
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('Create user error:', error);
|
||||
if (error.name === 'SequelizeUniqueConstraintError') {
|
||||
const field = error.errors[0]?.path || 'field';
|
||||
const value = error.errors[0]?.value || '';
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `${field} "${value}" already exists. Please use a unique value.`
|
||||
});
|
||||
}
|
||||
res.status(500).json({ success: false, message: 'Error creating user' });
|
||||
}
|
||||
};
|
||||
@ -282,8 +301,16 @@ export const updateUser = async (req: AuthRequest, res: Response) => {
|
||||
});
|
||||
|
||||
res.json({ success: true, message: 'User updated successfully', data: user });
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('Update user error:', error);
|
||||
if (error.name === 'SequelizeUniqueConstraintError') {
|
||||
const field = error.errors[0]?.path || 'field';
|
||||
const value = error.errors[0]?.value || '';
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `${field} "${value}" already exists. Please use a unique value.`
|
||||
});
|
||||
}
|
||||
res.status(500).json({ success: false, message: 'Error updating user' });
|
||||
}
|
||||
};
|
||||
|
||||
@ -38,18 +38,20 @@ export const createChecklist = async (req: AuthRequest, res: Response) => {
|
||||
});
|
||||
|
||||
if (created) {
|
||||
// Define Default Mandatory Items per SRS
|
||||
// Define Default Mandatory Items per SRS/Frontend
|
||||
const defaultItems = [
|
||||
{ itemType: 'Architecture', description: 'Brand Signage & Facade as per guidelines' },
|
||||
{ itemType: 'Architecture', description: 'Interior Fit-out & Furniture' },
|
||||
{ itemType: 'Sales', description: 'Display Vehicles (All models) available' },
|
||||
{ itemType: 'Sales', description: 'Test Ride Vehicles registered' },
|
||||
{ itemType: 'Training', description: 'Sales Staff (DSE) Training Completed' },
|
||||
{ itemType: 'Training', description: 'Service Technician (Pro-Meck) Training' },
|
||||
{ itemType: 'IT', description: 'DMS (Dealer Management System) configured' },
|
||||
{ itemType: 'IT', description: 'High-speed internet & IT Hardware ready' },
|
||||
{ itemType: 'Service', description: 'Special Tools & Equipment installed' },
|
||||
{ itemType: 'Finance', description: 'Bank Account mapped in SAP' }
|
||||
{ itemType: 'Sales', description: 'Sales Standards' },
|
||||
{ itemType: 'Service', description: 'Service & Spares' },
|
||||
{ itemType: 'IT', description: 'DMS infra' },
|
||||
{ itemType: 'Training', description: 'Manpower Training' },
|
||||
{ itemType: 'Statutory', description: 'Trade certificate with test ride bikes registration' },
|
||||
{ itemType: 'Statutory', description: 'GST certificate including Accessories & Apparels billing' },
|
||||
{ itemType: 'Finance', description: 'Inventory Funding' },
|
||||
{ itemType: 'IT', description: 'Virtual code availability' },
|
||||
{ itemType: 'Finance', description: 'Vendor payments' },
|
||||
{ itemType: 'Marketing', description: 'Details for website submission' },
|
||||
{ itemType: 'Insurance', description: 'Infra Insurance both Showroom and Service center' },
|
||||
{ itemType: 'IT', description: 'Auto ordering' }
|
||||
];
|
||||
|
||||
const itemsData = defaultItems.map(item => ({
|
||||
@ -75,15 +77,23 @@ export const updateItem = async (req: AuthRequest, res: Response) => {
|
||||
const { checklistId } = req.params;
|
||||
const { itemType, description, isCompliant, remarks, proofDocumentId } = req.body;
|
||||
|
||||
const item = await EorChecklistItem.create({
|
||||
checklistId,
|
||||
itemType,
|
||||
description,
|
||||
isCompliant,
|
||||
remarks,
|
||||
proofDocumentId
|
||||
let item = await EorChecklistItem.findOne({
|
||||
where: { checklistId, description }
|
||||
});
|
||||
|
||||
if (item) {
|
||||
await item.update({ isCompliant, remarks, proofDocumentId, itemType });
|
||||
} else {
|
||||
item = await EorChecklistItem.create({
|
||||
checklistId,
|
||||
itemType,
|
||||
description,
|
||||
isCompliant,
|
||||
remarks,
|
||||
proofDocumentId
|
||||
});
|
||||
}
|
||||
|
||||
res.status(201).json({ success: true, message: 'Item added/updated', data: item });
|
||||
} catch (error) {
|
||||
console.error('Update EOR item error:', error);
|
||||
|
||||
@ -194,7 +194,8 @@ 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.DealerCode, as: 'dealerCode' }
|
||||
]
|
||||
});
|
||||
|
||||
@ -297,6 +298,54 @@ export const uploadDocuments = async (req: any, res: Response) => {
|
||||
status: 'active'
|
||||
});
|
||||
|
||||
// Update architecture document date if relevant
|
||||
if (['Architecture Blueprint', 'Site Plan'].includes(documentType)) {
|
||||
await application.update({ architectureDocumentDate: new Date() });
|
||||
}
|
||||
|
||||
// Handle EOR Checklist Automatic Linking & Compliance
|
||||
const eorItems = [
|
||||
{ itemType: 'Sales', description: 'Sales Standards' },
|
||||
{ itemType: 'Service', description: 'Service & Spares' },
|
||||
{ itemType: 'IT', description: 'DMS infra' },
|
||||
{ itemType: 'Training', description: 'Manpower Training' },
|
||||
{ itemType: 'Statutory', description: 'Trade certificate with test ride bikes registration' },
|
||||
{ itemType: 'Statutory', description: 'GST certificate including Accessories & Apparels billing' },
|
||||
{ itemType: 'Finance', description: 'Inventory Funding' },
|
||||
{ itemType: 'IT', description: 'Virtual code availability' },
|
||||
{ itemType: 'Finance', description: 'Vendor payments' },
|
||||
{ itemType: 'Marketing', description: 'Details for website submission' },
|
||||
{ itemType: 'Insurance', description: 'Infra Insurance both Showroom and Service center' },
|
||||
{ itemType: 'IT', description: 'Auto ordering' }
|
||||
];
|
||||
|
||||
const eorDescriptions = eorItems.map(i => i.description);
|
||||
|
||||
if (eorDescriptions.includes(documentType)) {
|
||||
// Find or Create Checklist
|
||||
const [checklist, created] = await db.EorChecklist.findOrCreate({
|
||||
where: { applicationId: application.id },
|
||||
defaults: { status: 'In Progress' }
|
||||
});
|
||||
|
||||
// If newly created or no items exist, seed them
|
||||
const existingItemCount = await db.EorChecklistItem.count({ where: { checklistId: checklist.id } });
|
||||
if (created || existingItemCount === 0) {
|
||||
const itemsData = eorItems.map(item => ({
|
||||
...item,
|
||||
checklistId: checklist.id,
|
||||
isCompliant: false
|
||||
}));
|
||||
await db.EorChecklistItem.bulkCreate(itemsData);
|
||||
}
|
||||
|
||||
// Update the matching item - Link only, don't auto-verify (requested by user)
|
||||
await db.EorChecklistItem.update(
|
||||
{ proofDocumentId: newDoc.id, isCompliant: false },
|
||||
{ where: { checklistId: checklist.id, description: documentType } }
|
||||
);
|
||||
}
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Document uploaded successfully',
|
||||
@ -424,14 +473,21 @@ export const bulkShortlist = async (req: AuthRequest, res: Response) => {
|
||||
export const assignArchitectureTeam = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { userId, remarks } = req.body;
|
||||
const { userId, assignedTo, remarks } = req.body;
|
||||
const targetUserId = userId || assignedTo;
|
||||
|
||||
if (!targetUserId) {
|
||||
return res.status(400).json({ success: false, message: 'Architecture team member (userId) is required' });
|
||||
}
|
||||
|
||||
const application = await Application.findByPk(id);
|
||||
if (!application) return res.status(404).json({ success: false, message: 'Application not found' });
|
||||
|
||||
await application.update({
|
||||
architectureAssignedTo: userId,
|
||||
architectureStatus: 'Assigned',
|
||||
architectureAssignedTo: targetUserId,
|
||||
architectureStatus: 'IN_PROGRESS',
|
||||
architectureAssignedDate: new Date(),
|
||||
overallStatus: 'Architecture Team Assigned',
|
||||
updatedAt: new Date()
|
||||
});
|
||||
|
||||
@ -440,7 +496,7 @@ export const assignArchitectureTeam = async (req: AuthRequest, res: Response) =>
|
||||
where: {
|
||||
requestId: application.id,
|
||||
requestType: 'application',
|
||||
userId,
|
||||
userId: targetUserId,
|
||||
participantType: 'architecture'
|
||||
},
|
||||
defaults: { joinedMethod: 'auto' }
|
||||
@ -451,7 +507,7 @@ export const assignArchitectureTeam = async (req: AuthRequest, res: Response) =>
|
||||
action: AUDIT_ACTIONS.UPDATED,
|
||||
entityType: 'application',
|
||||
entityId: application.id,
|
||||
newData: { architectureAssignedTo: userId, remarks }
|
||||
newData: { architectureAssignedTo: targetUserId, remarks }
|
||||
});
|
||||
|
||||
res.json({ success: true, message: 'Architecture team assigned successfully' });
|
||||
@ -469,10 +525,20 @@ export const updateArchitectureStatus = async (req: AuthRequest, res: Response)
|
||||
const application = await Application.findByPk(id);
|
||||
if (!application) return res.status(404).json({ success: false, message: 'Application not found' });
|
||||
|
||||
await application.update({
|
||||
const updateData: any = {
|
||||
architectureStatus: status,
|
||||
updatedAt: new Date()
|
||||
});
|
||||
};
|
||||
|
||||
// Sync overall status if architecture is completed
|
||||
if (status === 'COMPLETED') {
|
||||
updateData.overallStatus = 'Architecture Team Completion';
|
||||
updateData.architectureCompletionDate = new Date();
|
||||
} else if (status === 'IN_PROGRESS') {
|
||||
updateData.overallStatus = 'Architecture Team Assigned';
|
||||
}
|
||||
|
||||
await application.update(updateData);
|
||||
|
||||
await AuditLog.create({
|
||||
userId: req.user?.id,
|
||||
@ -503,20 +569,44 @@ export const generateDealerCodes = async (req: AuthRequest, res: Response) => {
|
||||
|
||||
// Save Dealer Codes
|
||||
await db.DealerCode.create({
|
||||
dealerCode: sapData.salesCode, // Use sales code as primary dealer code
|
||||
applicationId: id,
|
||||
salesCode: sapData.salesCode,
|
||||
serviceCode: sapData.serviceCode,
|
||||
gmaCode: sapData.gmaCode,
|
||||
gearCode: sapData.gearCode,
|
||||
sapMasterId: sapData.sapMasterId,
|
||||
status: 'Active'
|
||||
status: 'Active',
|
||||
generatedBy: req.user?.id
|
||||
});
|
||||
|
||||
const previousStatus = application.overallStatus;
|
||||
|
||||
// Update application status to reflect codes are generated
|
||||
// We STAY in Dealer Code Generation until architecture is assigned
|
||||
await application.update({
|
||||
overallStatus: 'Architecture Team Assigned',
|
||||
overallStatus: 'Dealer Code Generation',
|
||||
progressPercentage: 80
|
||||
});
|
||||
|
||||
// Log Status History
|
||||
await db.ApplicationStatusHistory.create({
|
||||
applicationId: application.id,
|
||||
previousStatus,
|
||||
newStatus: 'Dealer Code Generation',
|
||||
changedBy: req.user?.id,
|
||||
reason: 'SAP Dealer Codes Generated'
|
||||
});
|
||||
|
||||
// Audit Log
|
||||
await db.AuditLog.create({
|
||||
userId: req.user?.id,
|
||||
action: AUDIT_ACTIONS.DEALER_CODE_GENERATED,
|
||||
entityType: 'application',
|
||||
entityId: application.id,
|
||||
newData: { dealerCode: sapData.salesCode }
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'SAP Dealer Codes generated successfully (Mock)',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user