started removing document type dependecy and f&F screeen bug changes fixed
This commit is contained in:
parent
f5d7ccc1ab
commit
9c6c585073
@ -2410,7 +2410,7 @@ documentation and AI recommendation summary.
|
||||
o FDD (Financial Due Diligence): Handled by the FDD partner for financial validation;
|
||||
documents uploaded for review.
|
||||
o LOI Approval: Preparation and verification for Letter of Intent issuance.
|
||||
o Security Details: Security and compliance document verification stage.
|
||||
o Security Deposit: Security and compliance document verification stage.
|
||||
o LOI Issue: Formal Letter of Intent generated and uploaded.
|
||||
o Dealer Code Generation : Dealer Code creation is initiated upon trigger by the DD-
|
||||
Admin , created in the SAP Master , and logged against the application for audit
|
||||
|
||||
83
scripts/migrate-onboarding-documents-cleanup.ts
Normal file
83
scripts/migrate-onboarding-documents-cleanup.ts
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Migration Script: Clean up onboarding_documents table.
|
||||
*
|
||||
* What it does (idempotent — safe to re-run):
|
||||
* 1. Drops legacy columns `requestId` and `requestType` (and their indexes).
|
||||
* These were generic catch-alls from when a single documents table routed
|
||||
* across modules. Each module now has its own dedicated documents table
|
||||
* (resignation_documents, termination_documents, constitutional_documents,
|
||||
* relocation_documents), so these columns are dead weight on
|
||||
* onboarding_documents and are not read or written anywhere in code.
|
||||
* 2. Adds two indexes the UI actually queries:
|
||||
* - (applicationId, stage) -> Progress / Documents tab grouping
|
||||
* - documentType -> EOR auto-link in onboarding.controller.ts
|
||||
*
|
||||
* What it does NOT do:
|
||||
* - No new "documentName" column. The user-entered document name is sent as
|
||||
* the FormData filename and stored in the existing `fileName` column.
|
||||
* - Does not touch `dealerId` (the Dealer <-> OnboardingDocument association
|
||||
* references it; it stays for future use).
|
||||
*
|
||||
* Run: npx tsx scripts/migrate-onboarding-documents-cleanup.ts
|
||||
*/
|
||||
import 'dotenv/config';
|
||||
import db from '../src/database/models/index.js';
|
||||
|
||||
const TABLE = 'onboarding_documents';
|
||||
|
||||
async function migrate() {
|
||||
const queryInterface = db.sequelize.getQueryInterface();
|
||||
|
||||
try {
|
||||
console.log(`🔄 Cleaning up ${TABLE} ...\n`);
|
||||
await db.sequelize.authenticate();
|
||||
|
||||
const tableInfo = await queryInterface.describeTable(TABLE);
|
||||
|
||||
// 1) Drop the index on requestId first (if it exists). Index name depends on
|
||||
// how Sequelize/Postgres generated it — try the common variants.
|
||||
for (const idxName of [
|
||||
`${TABLE}_requestId`,
|
||||
`${TABLE}_request_id`,
|
||||
]) {
|
||||
try {
|
||||
await db.sequelize.query(`DROP INDEX IF EXISTS "${idxName}"`);
|
||||
console.log(`✓ Dropped index ${idxName} (if existed)`);
|
||||
} catch (err: any) {
|
||||
console.log(`- Skipped index ${idxName}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Drop the unused columns (idempotent via describeTable check).
|
||||
for (const col of ['requestId', 'requestType']) {
|
||||
if (tableInfo[col]) {
|
||||
console.log(`Dropping column ${col} ...`);
|
||||
await queryInterface.removeColumn(TABLE, col);
|
||||
console.log(`✓ Dropped column ${col}`);
|
||||
} else {
|
||||
console.log(`- Column ${col} not present (already cleaned)`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Add useful indexes (idempotent — Postgres IF NOT EXISTS).
|
||||
await db.sequelize.query(
|
||||
`CREATE INDEX IF NOT EXISTS "${TABLE}_applicationId_stage" ON ${TABLE} ("applicationId", "stage")`
|
||||
);
|
||||
console.log(`✓ Ensured index ${TABLE}_applicationId_stage`);
|
||||
|
||||
await db.sequelize.query(
|
||||
`CREATE INDEX IF NOT EXISTS "${TABLE}_documentType" ON ${TABLE} ("documentType")`
|
||||
);
|
||||
console.log(`✓ Ensured index ${TABLE}_documentType`);
|
||||
|
||||
console.log('\n✅ Migration completed successfully!');
|
||||
} catch (error: any) {
|
||||
console.error('\n❌ Migration failed:', error.message);
|
||||
if (error.stack) console.error('\nStack Trace:\n', error.stack);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await db.sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
migrate();
|
||||
@ -5,8 +5,6 @@ export interface DocumentAttributes {
|
||||
id: string;
|
||||
applicationId: string | null;
|
||||
dealerId: string | null;
|
||||
requestId: string | null; // Compatibility
|
||||
requestType: string | null; // Compatibility
|
||||
documentType: string;
|
||||
fileName: string;
|
||||
filePath: string;
|
||||
@ -42,14 +40,6 @@ export default (sequelize: Sequelize) => {
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
requestId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true
|
||||
},
|
||||
requestType: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
documentType: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
@ -92,7 +82,8 @@ export default (sequelize: Sequelize) => {
|
||||
indexes: [
|
||||
{ fields: ['applicationId'] },
|
||||
{ fields: ['dealerId'] },
|
||||
{ fields: ['requestId'] }
|
||||
{ fields: ['applicationId', 'stage'] },
|
||||
{ fields: ['documentType'] }
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@ -1051,7 +1051,7 @@ export const updateResignationStatus = async (req: AuthRequest, res: Response, n
|
||||
return sendBackResignation(req, res, next);
|
||||
case 'pushfnf':
|
||||
// Verify if user role is authorized for manual jump to F&F
|
||||
const authorizedRoles = [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.DD_ADMIN, ROLES.SUPER_ADMIN];
|
||||
const authorizedRoles = [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.DD_ADMIN, ROLES.SUPER_ADMIN];
|
||||
if (!req.user || !authorizedRoles.includes(req.user.roleCode as any)) {
|
||||
return res.status(403).json({ success: false, message: 'You do not have permission to push this request to F&F' });
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ export class ResignationWorkflowService {
|
||||
[RESIGNATION_STAGES.NBH]: ROLES.NBH,
|
||||
[RESIGNATION_STAGES.LEGAL]: ROLES.LEGAL_ADMIN,
|
||||
[RESIGNATION_STAGES.DD_ADMIN]: ROLES.DD_ADMIN,
|
||||
[RESIGNATION_STAGES.AWAITING_FNF]: [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.DD_ADMIN],
|
||||
[RESIGNATION_STAGES.AWAITING_FNF]: [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.DD_ADMIN],
|
||||
[RESIGNATION_STAGES.FNF_INITIATED]: ROLES.DD_ADMIN
|
||||
};
|
||||
|
||||
|
||||
@ -296,55 +296,55 @@ async function triggerWorkflow() {
|
||||
remarks: 'Cleared Level 2'
|
||||
}, leadToken);
|
||||
log(5, 'Level 2 Complete.');
|
||||
// await delay();
|
||||
await delay();
|
||||
|
||||
// // 6. LEVEL-3 INTERVIEW
|
||||
// log(6, 'Scheduling Level 3 Interview...');
|
||||
// const headUser = users.data.find(u => u.email === EMAILS.DD_HEAD);
|
||||
// const nbhUser = users.data.find(u => u.email === EMAILS.NBH);
|
||||
// 6. LEVEL-3 INTERVIEW
|
||||
log(6, 'Scheduling Level 3 Interview...');
|
||||
const headUser = users.data.find(u => u.email === EMAILS.DD_HEAD);
|
||||
const nbhUser = users.data.find(u => u.email === EMAILS.NBH);
|
||||
|
||||
// const intv3Response = await apiRequest('/assessment/interviews', 'POST', {
|
||||
// applicationId: applicationUUID,
|
||||
// level: 3,
|
||||
// scheduledAt: new Date(Date.now() + 259200000).toISOString(),
|
||||
// type: 'In-Person',
|
||||
// location: 'HO',
|
||||
// participants: [headUser.id, nbhUser.id]
|
||||
// }, leadToken);
|
||||
// const interviewId3 = intv3Response.data.id;
|
||||
const intv3Response = await apiRequest('/assessment/interviews', 'POST', {
|
||||
applicationId: applicationUUID,
|
||||
level: 3,
|
||||
scheduledAt: new Date(Date.now() + 259200000).toISOString(),
|
||||
type: 'In-Person',
|
||||
location: 'HO',
|
||||
participants: [headUser.id, nbhUser.id]
|
||||
}, leadToken);
|
||||
const interviewId3 = intv3Response.data.id;
|
||||
|
||||
// log(6.1, 'NBH Giving Feedback...');
|
||||
// const nbhToken = await login(EMAILS.NBH);
|
||||
// await apiRequest('/assessment/level2-feedback', 'POST', {
|
||||
// interviewId: interviewId3,
|
||||
// overallScore: 10,
|
||||
// feedbackItems: [
|
||||
// { type: 'Business Vision & Strategy', comments: 'Highly recommended for this market.' },
|
||||
// { type: 'Leadership & Decision Making', comments: 'Shows great potential.' }
|
||||
// ],
|
||||
// recommendation: 'Selected'
|
||||
// }, nbhToken);
|
||||
log(6.1, 'NBH Giving Feedback...');
|
||||
const nbhToken = await login(EMAILS.NBH);
|
||||
await apiRequest('/assessment/level2-feedback', 'POST', {
|
||||
interviewId: interviewId3,
|
||||
overallScore: 10,
|
||||
feedbackItems: [
|
||||
{ type: 'Business Vision & Strategy', comments: 'Highly recommended for this market.' },
|
||||
{ type: 'Leadership & Decision Making', comments: 'Shows great potential.' }
|
||||
],
|
||||
recommendation: 'Selected'
|
||||
}, nbhToken);
|
||||
|
||||
// log(6.15, 'DD-Head Giving Feedback...');
|
||||
// const headToken = await login(EMAILS.DD_HEAD);
|
||||
// await apiRequest('/assessment/level2-feedback', 'POST', {
|
||||
// interviewId: interviewId3,
|
||||
// overallScore: 9.5,
|
||||
// feedbackItems: [
|
||||
// { type: 'Operational & Financial Readiness', comments: 'Financially sound.' },
|
||||
// { type: 'Brand Alignment', comments: 'Understands Royal Enfield ethos perfectly.' }
|
||||
// ],
|
||||
// recommendation: 'Selected'
|
||||
// }, headToken);
|
||||
log(6.15, 'DD-Head Giving Feedback...');
|
||||
const headToken = await login(EMAILS.DD_HEAD);
|
||||
await apiRequest('/assessment/level2-feedback', 'POST', {
|
||||
interviewId: interviewId3,
|
||||
overallScore: 9.5,
|
||||
feedbackItems: [
|
||||
{ type: 'Operational & Financial Readiness', comments: 'Financially sound.' },
|
||||
{ type: 'Brand Alignment', comments: 'Understands Royal Enfield ethos perfectly.' }
|
||||
],
|
||||
recommendation: 'Selected'
|
||||
}, headToken);
|
||||
|
||||
// log(6.2, 'Head Finalizing Level 3 Decision...');
|
||||
// await apiRequest('/assessment/decision', 'POST', {
|
||||
// interviewId: interviewId3,
|
||||
// decision: 'Approved',
|
||||
// remarks: 'Cleared Level 3. Moving to FDD.'
|
||||
// }, headToken);
|
||||
// log(6, 'Level 3 Complete. Stage is now FDD Verification.');
|
||||
// await delay();
|
||||
log(6.2, 'Head Finalizing Level 3 Decision...');
|
||||
await apiRequest('/assessment/decision', 'POST', {
|
||||
interviewId: interviewId3,
|
||||
decision: 'Approved',
|
||||
remarks: 'Cleared Level 3. Moving to FDD.'
|
||||
}, headToken);
|
||||
log(6, 'Level 3 Complete. Stage is now FDD Verification.');
|
||||
await delay();
|
||||
|
||||
// // 6.3 FDD ASSIGNMENT
|
||||
// log(6.3, 'Admin Assigning Application to FDD Agency...');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user