From 9c6c585073a598734eda7ab2cbec1e8d6a2d6734 Mon Sep 17 00:00:00 2001 From: Laxman Date: Tue, 19 May 2026 21:25:24 +0530 Subject: [PATCH] started removing document type dependecy and f&F screeen bug changes fixed --- docs/modular_wise/01_Dealer_Onboarding.md | 2 +- .../migrate-onboarding-documents-cleanup.ts | 83 +++++++++++++++++ .../models/application/OnboardingDocument.ts | 13 +-- .../self-service/resignation.controller.ts | 2 +- src/services/ResignationWorkflowService.ts | 2 +- trigger-workflow.js | 88 +++++++++---------- 6 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 scripts/migrate-onboarding-documents-cleanup.ts diff --git a/docs/modular_wise/01_Dealer_Onboarding.md b/docs/modular_wise/01_Dealer_Onboarding.md index 2c3ebfa..373e77d 100644 --- a/docs/modular_wise/01_Dealer_Onboarding.md +++ b/docs/modular_wise/01_Dealer_Onboarding.md @@ -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 diff --git a/scripts/migrate-onboarding-documents-cleanup.ts b/scripts/migrate-onboarding-documents-cleanup.ts new file mode 100644 index 0000000..1d46c76 --- /dev/null +++ b/scripts/migrate-onboarding-documents-cleanup.ts @@ -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(); diff --git a/src/database/models/application/OnboardingDocument.ts b/src/database/models/application/OnboardingDocument.ts index a8f51a3..5e1a556 100644 --- a/src/database/models/application/OnboardingDocument.ts +++ b/src/database/models/application/OnboardingDocument.ts @@ -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'] } ] }); diff --git a/src/modules/self-service/resignation.controller.ts b/src/modules/self-service/resignation.controller.ts index 09ce826..8c3d063 100644 --- a/src/modules/self-service/resignation.controller.ts +++ b/src/modules/self-service/resignation.controller.ts @@ -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' }); } diff --git a/src/services/ResignationWorkflowService.ts b/src/services/ResignationWorkflowService.ts index 9376978..d21eabc 100644 --- a/src/services/ResignationWorkflowService.ts +++ b/src/services/ResignationWorkflowService.ts @@ -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 }; diff --git a/trigger-workflow.js b/trigger-workflow.js index 4a1b4ba..d9ea2dd 100644 --- a/trigger-workflow.js +++ b/trigger-workflow.js @@ -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...');