bugs were covered
This commit is contained in:
parent
e99a28b7f7
commit
f5022b613d
@ -43,7 +43,7 @@ async function reset() {
|
|||||||
await db.ApplicationProgress.destroy({
|
await db.ApplicationProgress.destroy({
|
||||||
where: {
|
where: {
|
||||||
applicationId: app.id,
|
applicationId: app.id,
|
||||||
stageName: ['LOI Approval', 'Security Details', 'LOI Issue', 'Dealer Code Generation', 'Architecture Team Assigned', 'Statutory GST']
|
stageName: ['LOI Approval', 'Security Deposit', 'LOI Issue', 'Dealer Code Generation', 'Architecture Team Assigned', 'Statutory GST']
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
18
scripts/sql/application_security_deposit_status_rename.sql
Normal file
18
scripts/sql/application_security_deposit_status_rename.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
-- Rename overallStatus value Security Details -> Security Deposit (canonical label from APPLICATION_STATUS.SECURITY_DETAILS).
|
||||||
|
-- Run once per environment AFTER deploying code that uses 'Security Deposit'.
|
||||||
|
--
|
||||||
|
-- 1) Discover enum type name for applications.overallStatus:
|
||||||
|
-- SELECT c.column_name, c.udt_name
|
||||||
|
-- FROM information_schema.columns c
|
||||||
|
-- WHERE c.table_schema = 'public' AND c.table_name = 'applications' AND c.column_name = 'overallStatus';
|
||||||
|
--
|
||||||
|
-- 2) Add new enum value (PostgreSQL 9.1+). Replace type name if yours differs.
|
||||||
|
-- ALTER TYPE "enum_applications_overallStatus" ADD VALUE IF NOT EXISTS 'Security Deposit';
|
||||||
|
--
|
||||||
|
-- 3) Backfill rows (camelCase column is typical for this Sequelize project).
|
||||||
|
-- UPDATE applications SET "overallStatus" = 'Security Deposit' WHERE "overallStatus" = 'Security Details';
|
||||||
|
--
|
||||||
|
-- 4) Backfill ApplicationProgress pipeline stage label (see ONBOARDING_STAGES in progress.ts).
|
||||||
|
-- UPDATE application_progress SET "stageName" = 'Security Deposit' WHERE "stageName" = 'Security Details';
|
||||||
|
|
||||||
|
-- Minimal safe block: only step 3+4 if you use VARCHAR-like enums; for native ENUM you must run step 2 first (cannot be in same transaction as use of new value on older PG — run ADD VALUE, COMMIT, then UPDATE).
|
||||||
@ -85,7 +85,7 @@ export const APPLICATION_STATUS = {
|
|||||||
LEVEL_3_PENDING: 'Level 3 Interview Pending',
|
LEVEL_3_PENDING: 'Level 3 Interview Pending',
|
||||||
LEVEL_3_APPROVED: 'Level 3 Approved',
|
LEVEL_3_APPROVED: 'Level 3 Approved',
|
||||||
FDD_VERIFICATION: 'FDD Verification',
|
FDD_VERIFICATION: 'FDD Verification',
|
||||||
SECURITY_DETAILS: 'Security Details',
|
SECURITY_DETAILS: 'Security Deposit',
|
||||||
PAYMENT_PENDING: 'Payment Pending',
|
PAYMENT_PENDING: 'Payment Pending',
|
||||||
LOI_IN_PROGRESS: 'LOI In Progress',
|
LOI_IN_PROGRESS: 'LOI In Progress',
|
||||||
LOI_ISSUED: 'LOI Issued',
|
LOI_ISSUED: 'LOI Issued',
|
||||||
@ -142,6 +142,8 @@ export const OVERALL_STATUS_TO_DB_CURRENT_STAGE: Record<
|
|||||||
[APPLICATION_STATUS.LEVEL_3_APPROVED]: APPLICATION_STAGES.LEVEL_3_APPROVED,
|
[APPLICATION_STATUS.LEVEL_3_APPROVED]: APPLICATION_STAGES.LEVEL_3_APPROVED,
|
||||||
[APPLICATION_STATUS.FDD_VERIFICATION]: APPLICATION_STAGES.FDD,
|
[APPLICATION_STATUS.FDD_VERIFICATION]: APPLICATION_STAGES.FDD,
|
||||||
[APPLICATION_STATUS.SECURITY_DETAILS]: APPLICATION_STAGES.LOI,
|
[APPLICATION_STATUS.SECURITY_DETAILS]: APPLICATION_STAGES.LOI,
|
||||||
|
/** Legacy `overallStatus` before rename; remove after DB migrated */
|
||||||
|
'Security Details': APPLICATION_STAGES.LOI,
|
||||||
[APPLICATION_STATUS.PAYMENT_PENDING]: APPLICATION_STAGES.LOI,
|
[APPLICATION_STATUS.PAYMENT_PENDING]: APPLICATION_STAGES.LOI,
|
||||||
[APPLICATION_STATUS.LOI_IN_PROGRESS]: APPLICATION_STAGES.LOI,
|
[APPLICATION_STATUS.LOI_IN_PROGRESS]: APPLICATION_STAGES.LOI,
|
||||||
[APPLICATION_STATUS.LOI_ISSUED]: APPLICATION_STAGES.LOI,
|
[APPLICATION_STATUS.LOI_ISSUED]: APPLICATION_STAGES.LOI,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const ONBOARDING_STAGES = [
|
|||||||
{ name: '3rd Level Interview', order: 6 },
|
{ name: '3rd Level Interview', order: 6 },
|
||||||
{ name: 'FDD', order: 7 },
|
{ name: 'FDD', order: 7 },
|
||||||
{ name: 'LOI Approval', order: 8 },
|
{ name: 'LOI Approval', order: 8 },
|
||||||
{ name: 'Security Details', order: 9 },
|
{ name: 'Security Deposit', order: 9 },
|
||||||
{ name: 'LOI Issue', order: 10 },
|
{ name: 'LOI Issue', order: 10 },
|
||||||
{ name: 'Dealer Code Generation', order: 11 },
|
{ name: 'Dealer Code Generation', order: 11 },
|
||||||
{ name: 'Architecture Work', order: 12 },
|
{ name: 'Architecture Work', order: 12 },
|
||||||
@ -107,8 +107,10 @@ export const PIPELINE_STAGE_LABEL_BY_OVERALL_STATUS: Record<string, string> = {
|
|||||||
'Level 3 Approved': '3rd Level Interview',
|
'Level 3 Approved': '3rd Level Interview',
|
||||||
'FDD Verification': 'FDD',
|
'FDD Verification': 'FDD',
|
||||||
'LOI In Progress': 'LOI Approval',
|
'LOI In Progress': 'LOI Approval',
|
||||||
'Security Details': 'Security Details',
|
/** @deprecated DB rows may still use pre-rename label until migrated */
|
||||||
'Payment Pending': 'Security Details',
|
'Security Details': 'Security Deposit',
|
||||||
|
'Security Deposit': 'Security Deposit',
|
||||||
|
'Payment Pending': 'Security Deposit',
|
||||||
'LOI Issued': 'LOI Issue',
|
'LOI Issued': 'LOI Issue',
|
||||||
'Statutory LOI Ack': 'LOI Issue',
|
'Statutory LOI Ack': 'LOI Issue',
|
||||||
'Dealer Code Generation': 'Dealer Code Generation',
|
'Dealer Code Generation': 'Dealer Code Generation',
|
||||||
|
|||||||
@ -51,7 +51,7 @@ const ACTION_DESCRIPTIONS: Record<string, string> = {
|
|||||||
DEALER_UPDATED: 'Dealer profile updated',
|
DEALER_UPDATED: 'Dealer profile updated',
|
||||||
DEALER_CODE_GENERATED: 'Dealer code generated',
|
DEALER_CODE_GENERATED: 'Dealer code generated',
|
||||||
PAYMENT_UPDATED: 'Payment record updated',
|
PAYMENT_UPDATED: 'Payment record updated',
|
||||||
SECURITY_DEPOSIT_UPDATED: 'Security deposit updated',
|
SECURITY_DEPOSIT_UPDATED: 'Security Deposit updated',
|
||||||
FNF_UPDATED: 'F&F settlement updated',
|
FNF_UPDATED: 'F&F settlement updated',
|
||||||
CLEARANCE_UPDATED: 'Departmental clearance response recorded',
|
CLEARANCE_UPDATED: 'Departmental clearance response recorded',
|
||||||
STAKEHOLDER_CLEARANCE_UPDATED: 'F&F stakeholder clearance synced',
|
STAKEHOLDER_CLEARANCE_UPDATED: 'F&F stakeholder clearance synced',
|
||||||
|
|||||||
@ -451,13 +451,15 @@ export const updateSecurityDeposit = async (req: AuthRequest, res: Response) =>
|
|||||||
if ((depositType === 'SECURITY_DEPOSIT' || !depositType) && status === 'Verified') {
|
if ((depositType === 'SECURITY_DEPOSIT' || !depositType) && status === 'Verified') {
|
||||||
const os = application.overallStatus;
|
const os = application.overallStatus;
|
||||||
const inInitialSdCorridor =
|
const inInitialSdCorridor =
|
||||||
os === APPLICATION_STATUS.PAYMENT_PENDING || os === APPLICATION_STATUS.SECURITY_DETAILS;
|
os === APPLICATION_STATUS.PAYMENT_PENDING ||
|
||||||
|
os === APPLICATION_STATUS.SECURITY_DETAILS ||
|
||||||
|
os === 'Security Details';
|
||||||
if (inInitialSdCorridor) {
|
if (inInitialSdCorridor) {
|
||||||
console.log(
|
console.log(
|
||||||
`[DEBUG] SECURITY_DEPOSIT verified (overallStatus=${os}). Aligning to Security Details for admin before LOI Issued.`,
|
`[DEBUG] SECURITY_DEPOSIT verified (overallStatus=${os}). Aligning to Security Deposit for admin before LOI Issued.`,
|
||||||
);
|
);
|
||||||
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.SECURITY_DETAILS, req.user?.id || null, {
|
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.SECURITY_DETAILS, req.user?.id || null, {
|
||||||
reason: 'Security deposit verified by Finance. Awaiting admin approval to proceed to LOI issuance.',
|
reason: 'Security Deposit verified by Finance. Awaiting admin approval to proceed to LOI issuance.',
|
||||||
stage: APPLICATION_STAGES.LOI,
|
stage: APPLICATION_STAGES.LOI,
|
||||||
progressPercentage: 78
|
progressPercentage: 78
|
||||||
});
|
});
|
||||||
@ -492,7 +494,7 @@ export const updateSecurityDeposit = async (req: AuthRequest, res: Response) =>
|
|||||||
res.json({ success: true, message: 'Security Deposit updated', data: updatedDeposit });
|
res.json({ success: true, message: 'Security Deposit updated', data: updatedDeposit });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Update Security Deposit error:', error);
|
console.error('Update Security Deposit error:', error);
|
||||||
res.status(500).json({ success: false, message: 'Error updating security deposit' });
|
res.status(500).json({ success: false, message: 'Error updating Security Deposit' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -517,6 +519,6 @@ export const getSecurityDeposit = async (req: Request, res: Response) => {
|
|||||||
res.json({ success: true, data: deposits });
|
res.json({ success: true, data: deposits });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fetch Security Deposit error:', error);
|
console.error('Fetch Security Deposit error:', error);
|
||||||
res.status(500).json({ success: false, message: 'Error fetching security deposit' });
|
res.status(500).json({ success: false, message: 'Error fetching Security Deposit' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -111,8 +111,8 @@ export const acknowledgeRequest = async (req: AuthRequest, res: Response) => {
|
|||||||
const phone = (applicantUser as any)?.mobileNumber || application.phone || '';
|
const phone = (applicantUser as any)?.mobileNumber || application.phone || '';
|
||||||
|
|
||||||
await NotificationService.notify(applicantUser?.id ?? null, application.email, {
|
await NotificationService.notify(applicantUser?.id ?? null, application.email, {
|
||||||
title: `Security deposit — ${application.applicationId}`,
|
title: `Security Deposit — ${application.applicationId}`,
|
||||||
message: `Please remit the security deposit for ${application.applicationId} as per your LOI.`,
|
message: `Please remit the Security Deposit for ${application.applicationId} as per your LOI.`,
|
||||||
channels: phone ? ['email', 'whatsapp'] : ['email'],
|
channels: phone ? ['email', 'whatsapp'] : ['email'],
|
||||||
templateCode: 'SECURITY_DEPOSIT_REQUEST',
|
templateCode: 'SECURITY_DEPOSIT_REQUEST',
|
||||||
placeholders: {
|
placeholders: {
|
||||||
@ -125,7 +125,7 @@ export const acknowledgeRequest = async (req: AuthRequest, res: Response) => {
|
|||||||
ctaLabel: 'Pay or upload proof',
|
ctaLabel: 'Pay or upload proof',
|
||||||
phone: String(phone || '')
|
phone: String(phone || '')
|
||||||
}
|
}
|
||||||
}).catch((e: any) => console.error('[LOI] Security deposit email failed:', e));
|
}).catch((e: any) => console.error('[LOI] Security Deposit email failed:', e));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({ success: true, message: 'LOI Acknowledged by applicant' });
|
res.json({ success: true, message: 'LOI Acknowledged by applicant' });
|
||||||
|
|||||||
@ -145,7 +145,7 @@ const seedTemplates = async () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
templateCode: 'ONBOARDING_PAYMENT_VERIFIED',
|
templateCode: 'ONBOARDING_PAYMENT_VERIFIED',
|
||||||
description: 'Notification when security deposit or initial payment is verified',
|
description: 'Notification when Security Deposit or initial payment is verified',
|
||||||
subject: 'Payment Verified: {{applicationId}}',
|
subject: 'Payment Verified: {{applicationId}}',
|
||||||
fileName: 'onboarding_payment_verified.html',
|
fileName: 'onboarding_payment_verified.html',
|
||||||
placeholders: ['applicationId', 'dealerName', 'paymentType', 'amount', 'link']
|
placeholders: ['applicationId', 'dealerName', 'paymentType', 'amount', 'link']
|
||||||
@ -355,7 +355,7 @@ const seedTemplates = async () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
templateCode: 'SECURITY_DEPOSIT_REQUEST',
|
templateCode: 'SECURITY_DEPOSIT_REQUEST',
|
||||||
description: 'After LOI acknowledgement: security deposit payment instructions',
|
description: 'After LOI acknowledgement: Security Deposit payment instructions',
|
||||||
subject: 'Security Deposit Payment Required — {{applicationId}}',
|
subject: 'Security Deposit Payment Required — {{applicationId}}',
|
||||||
fileName: 'security_deposit_request.html',
|
fileName: 'security_deposit_request.html',
|
||||||
placeholders: ['applicantName', 'applicationId', 'amount', 'dueDate', 'bankDetails', 'link', 'ctaLabel']
|
placeholders: ['applicantName', 'applicationId', 'amount', 'dueDate', 'bankDetails', 'link', 'ctaLabel']
|
||||||
|
|||||||
@ -26,7 +26,14 @@ export class WorkflowIntegrityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LOI STAGE INTEGRITY
|
// LOI STAGE INTEGRITY
|
||||||
if ([APPLICATION_STATUS.LOI_IN_PROGRESS, APPLICATION_STATUS.PAYMENT_PENDING, APPLICATION_STATUS.SECURITY_DETAILS].includes(application.overallStatus)) {
|
// Include legacy DB label "Security Details" until data migration completes.
|
||||||
|
const loiPreIssuedStatuses = [
|
||||||
|
APPLICATION_STATUS.LOI_IN_PROGRESS,
|
||||||
|
APPLICATION_STATUS.PAYMENT_PENDING,
|
||||||
|
APPLICATION_STATUS.SECURITY_DETAILS,
|
||||||
|
'Security Details'
|
||||||
|
];
|
||||||
|
if (loiPreIssuedStatuses.includes(application.overallStatus)) {
|
||||||
await this.syncLoiIntegrity(application);
|
await this.syncLoiIntegrity(application);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +121,7 @@ export class WorkflowIntegrityService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (policyMet && deposit) {
|
if (policyMet && deposit) {
|
||||||
console.log(`[WorkflowIntegrityService] Policy met and payment verified for LOI on ${application.applicationId}. Aligning to Security Details for admin approval before LOI Issued.`);
|
console.log(`[WorkflowIntegrityService] Policy met and payment verified for LOI on ${application.applicationId}. Aligning to Security Deposit for admin approval before LOI Issued.`);
|
||||||
|
|
||||||
// Ensure LoiRequest is also updated
|
// Ensure LoiRequest is also updated
|
||||||
const request = await db.LoiRequest.findOne({ where: { applicationId: application.id } });
|
const request = await db.LoiRequest.findOne({ where: { applicationId: application.id } });
|
||||||
@ -123,7 +130,7 @@ export class WorkflowIntegrityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.SECURITY_DETAILS, null, {
|
await WorkflowService.transitionApplication(application, APPLICATION_STATUS.SECURITY_DETAILS, null, {
|
||||||
reason: 'Integrity sync: LOI policy and deposit verified — use Security Details admin approval to reach LOI Issued.',
|
reason: 'Integrity sync: LOI policy and deposit verified — use Security Deposit admin approval to reach LOI Issued.',
|
||||||
progressPercentage: 78
|
progressPercentage: 78
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -426,8 +426,8 @@ async function triggerWorkflow() {
|
|||||||
await ensureMandatoryCodeGenFields(applicationUUID, adminToken);
|
await ensureMandatoryCodeGenFields(applicationUUID, adminToken);
|
||||||
await delay(300);
|
await delay(300);
|
||||||
|
|
||||||
if (statusBeforeCodeGen === 'Security Details') {
|
if (statusBeforeCodeGen === 'Security Deposit' || statusBeforeCodeGen === 'Security Details') {
|
||||||
log(9, 'Status is Security Details; re-verifying Security Deposit to move to LOI Issued...');
|
log(9, 'Status is Security Deposit (or legacy Security Details); re-verifying Security Deposit to move to LOI Issued...');
|
||||||
await apiRequest('/loa/security-deposit', 'POST', {
|
await apiRequest('/loa/security-deposit', 'POST', {
|
||||||
applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
amount: 500000,
|
amount: 500000,
|
||||||
@ -440,13 +440,13 @@ async function triggerWorkflow() {
|
|||||||
log(9, `Status after re-verify: ${statusBeforeCodeGen}`);
|
log(9, `Status after re-verify: ${statusBeforeCodeGen}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current backend flow keeps app at "Security Details" until explicit admin transition.
|
// Current backend flow keeps app at Security Deposit until explicit admin transition.
|
||||||
if (statusBeforeCodeGen === 'Security Details') {
|
if (statusBeforeCodeGen === 'Security Deposit' || statusBeforeCodeGen === 'Security Details') {
|
||||||
log(9, 'Applying admin transition from Security Details -> LOI Issued...');
|
log(9, 'Applying admin transition from Security Deposit -> LOI Issued...');
|
||||||
await apiRequest(`/onboarding/applications/${applicationUUID}/status`, 'PUT', {
|
await apiRequest(`/onboarding/applications/${applicationUUID}/status`, 'PUT', {
|
||||||
status: 'LOI Issued',
|
status: 'LOI Issued',
|
||||||
stage: 'LOI',
|
stage: 'LOI',
|
||||||
reason: 'E2E script alignment: unlock dealer code generation after Security Details checks.'
|
reason: 'E2E script alignment: unlock dealer code generation after Security Deposit checks.'
|
||||||
}, adminToken);
|
}, adminToken);
|
||||||
await delay();
|
await delay();
|
||||||
statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user