bugs coverd

This commit is contained in:
Laxman 2026-05-15 20:20:15 +05:30
parent 1340f44485
commit 81d4dd493f
14 changed files with 44 additions and 30 deletions

View File

@ -273,7 +273,7 @@ export default function App() {
return 'Payment schedule, proofs, and audit notes for this onboarding application.'; return 'Payment schedule, proofs, and audit notes for this onboarding application.';
} }
if (pathname === '/finance-onboarding') { if (pathname === '/finance-onboarding') {
return 'Validate security deposits, first fills, and related onboarding payments.'; return 'Validate Security Deposit, first fills, and related onboarding payments.';
} }
if (pathname.startsWith('/finance-audit/')) { if (pathname.startsWith('/finance-audit/')) {
return 'Finance audit trail and checklist for this application.'; return 'Finance audit trail and checklist for this application.';

View File

@ -52,14 +52,14 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
const apps = response.data || []; const apps = response.data || [];
// Derive Onboarding Payments from Application + SecurityDeposit (Standardized nomenclature) // Derive Onboarding Payments from Application + SecurityDeposit (Standardized nomenclature)
// This ensures applications in "Payment Pending" / "Security Details" are visible // This ensures applications in "Payment Pending" / "Security Deposit" are visible
// even if no payment record has been manually initialized. // even if no payment record has been manually initialized.
const consolidatedPayments: any[] = []; const consolidatedPayments: any[] = [];
apps.forEach((app: any) => { apps.forEach((app: any) => {
const s = app.overallStatus || app.status; const s = app.overallStatus || app.status;
const isPaymentStage = [ const isPaymentStage = [
'Payment Pending', 'Security Details', 'LOI In Progress', 'LOI Issued', 'Payment Pending', 'Security Deposit', 'Security Details', 'LOI In Progress', 'LOI Issued',
'LOA Pending', 'Dealer Code Generation', 'LOA_APPROVAL' 'LOA Pending', 'Dealer Code Generation', 'LOA_APPROVAL'
].includes(s); ].includes(s);
const deposits = app.securityDeposits || []; const deposits = app.securityDeposits || [];
@ -80,7 +80,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
}); });
}); });
} else if (isPaymentStage) { } else if (isPaymentStage) {
if (['Payment Pending', 'Security Details', 'LOI In Progress'].includes(s)) { if (['Payment Pending', 'Security Deposit', 'Security Details', 'LOI In Progress'].includes(s)) {
// Virtual pending record for Security Deposit (5L) // Virtual pending record for Security Deposit (5L)
consolidatedPayments.push({ consolidatedPayments.push({
id: `virtual-${app.id}-sd`, id: `virtual-${app.id}-sd`,

View File

@ -73,7 +73,7 @@ export const SecurityDepositMaster: React.FC = () => {
Global Payment Settings Global Payment Settings
</CardTitle> </CardTitle>
<CardDescription className="text-xs"> <CardDescription className="text-xs">
Configure base security deposit amounts for onboarding workflows. Configure base Security Deposit amounts for onboarding workflows.
</CardDescription> </CardDescription>
</div> </div>
</div> </div>

View File

@ -177,7 +177,7 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
<AlertTitle className="text-amber-900 font-semibold">LOA approval locked</AlertTitle> <AlertTitle className="text-amber-900 font-semibold">LOA approval locked</AlertTitle>
<AlertDescription className="text-amber-800"> <AlertDescription className="text-amber-800">
<span className="font-medium">First Fill</span> (later-stage payment) must be verified by Finance <span className="font-medium">First Fill</span> (later-stage payment) must be verified by Finance
before LOA approval can proceed. This is separate from the initial security deposit before LOI Issued. before LOA approval can proceed. This is separate from the initial Security Deposit before LOI Issued.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
)} )}
@ -194,7 +194,7 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
Finance has verified the <span className="font-medium">First Fill</span> payment. The application Finance has verified the <span className="font-medium">First Fill</span> payment. The application
status was <span className="font-medium">not</span> changed until you reach{' '} status was <span className="font-medium">not</span> changed until you reach{' '}
<span className="font-medium">LOA Pending</span>. When you get there, LOA approval will not be <span className="font-medium">LOA Pending</span>. When you get there, LOA approval will not be
blocked by payment (same pattern as recording the initial security deposit before the LOI blocked by payment (same pattern as recording the initial Security Deposit before the LOI
security step). security step).
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@ -203,7 +203,7 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
{permissions.isSecurityDetailsLocked && ( {permissions.isSecurityDetailsLocked && (
<Alert variant="destructive" className="mb-4 bg-amber-50 border-amber-200 text-amber-800" data-testid="onboarding-details-security-locked-alert"> <Alert variant="destructive" className="mb-4 bg-amber-50 border-amber-200 text-amber-800" data-testid="onboarding-details-security-locked-alert">
<Lock className="w-4 h-4 text-amber-600" /> <Lock className="w-4 h-4 text-amber-600" />
<AlertTitle className="text-amber-900 font-semibold">Security Details approval locked</AlertTitle> <AlertTitle className="text-amber-900 font-semibold">Security Deposit approval locked</AlertTitle>
<AlertDescription className="text-amber-800"> <AlertDescription className="text-amber-800">
Finance must verify the <span className="font-medium">Security Deposit</span> before this stage can be approved. Finance must verify the <span className="font-medium">Security Deposit</span> before this stage can be approved.
You can still use <span className="font-medium">Reject</span> if needed. You can still use <span className="font-medium">Reject</span> if needed.
@ -211,12 +211,12 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
</Alert> </Alert>
)} )}
{['Security Details', 'Payment Pending'].includes(application.status) && ( {['Security Deposit', 'Security Details', 'Payment Pending'].includes(application.status) && (
<Alert className="mb-4 border-sky-200 bg-sky-50/90 text-sky-900" data-testid="onboarding-details-security-review-alert"> <Alert className="mb-4 border-sky-200 bg-sky-50/90 text-sky-900" data-testid="onboarding-details-security-review-alert">
<Info className="h-4 w-4 text-sky-700" /> <Info className="h-4 w-4 text-sky-700" />
<AlertTitle className="text-sky-950 font-semibold">Security Details review</AlertTitle> <AlertTitle className="text-sky-950 font-semibold">Security Deposit review</AlertTitle>
<AlertDescription className="text-sm text-sky-900/90 leading-relaxed"> <AlertDescription className="text-sm text-sky-900/90 leading-relaxed">
Check the initial security deposit on the <span className="font-medium">Payments</span> tab (Finance Check the initial Security Deposit on the <span className="font-medium">Payments</span> tab (Finance
may have already marked it verified). When satisfied, use <span className="font-medium">Approve</span>{' '} may have already marked it verified). When satisfied, use <span className="font-medium">Approve</span>{' '}
to move to <span className="font-medium">LOI Issued</span>. to move to <span className="font-medium">LOI Issued</span>.
</AlertDescription> </AlertDescription>

View File

@ -415,7 +415,8 @@ export function useApplicationDetailsAdminActions(params: UseApplicationDetailsA
case 'Level 3 Interview Pending': newStatus = 'Level 3 Approved'; break; case 'Level 3 Interview Pending': newStatus = 'Level 3 Approved'; break;
case 'Level 3 Approved': newStatus = 'FDD Verification'; break; case 'Level 3 Approved': newStatus = 'FDD Verification'; break;
case 'FDD Verification': newStatus = 'LOI In Progress'; break; case 'FDD Verification': newStatus = 'LOI In Progress'; break;
case 'LOI In Progress': newStatus = 'Security Details'; break; case 'LOI In Progress': newStatus = 'Security Deposit'; break;
case 'Security Deposit':
case 'Security Details': case 'Security Details':
case 'Payment Pending': newStatus = 'LOI Issued'; break; case 'Payment Pending': newStatus = 'LOI Issued'; break;
case 'LOI Issued': newStatus = 'Dealer Code Generation'; break; case 'LOI Issued': newStatus = 'Dealer Code Generation'; break;

View File

@ -84,7 +84,9 @@ export function useApplicationDetailsData({ applicationId }: UseApplicationDetai
level3InterviewDate: getStageDate('3rd Level Interview', 'Level 3 Approved'), level3InterviewDate: getStageDate('3rd Level Interview', 'Level 3 Approved'),
fddDate: getStageDate('FDD', 'FDD Verification'), fddDate: getStageDate('FDD', 'FDD Verification'),
loiApprovalDate: getStageDate('LOI Approval', 'LOI In Progress'), loiApprovalDate: getStageDate('LOI Approval', 'LOI In Progress'),
securityDetailsDate: getStageDate('Security Details', 'Security Details'), securityDetailsDate:
getStageDate('Security Deposit', 'Security Deposit') ||
getStageDate('Security Details', 'Security Details'),
loiIssueDate: getStageDate('LOI Issue', 'LOI Issued'), loiIssueDate: getStageDate('LOI Issue', 'LOI Issued'),
dealerCodeDate: getStageDate('Dealer Code Generation', 'Dealer Code Generation'), dealerCodeDate: getStageDate('Dealer Code Generation', 'Dealer Code Generation'),
architectureAssignedDate: getStageDate('Architecture Team Assigned', 'Architecture Team Assigned'), architectureAssignedDate: getStageDate('Architecture Team Assigned', 'Architecture Team Assigned'),

View File

@ -188,7 +188,7 @@ export function useApplicationDetailsPermissions({
const isAdminRole = ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head', 'Finance', 'Finance Admin', 'FDD', 'ZBH', 'RBM'].includes(currentUser.role); const isAdminRole = ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head', 'Finance', 'Finance Admin', 'FDD', 'ZBH', 'RBM'].includes(currentUser.role);
const isAdministrativeStage = [ const isAdministrativeStage = [
'Level 3 Approved', 'FDD Verification', 'Level 3 Approved', 'FDD Verification',
'LOI In Progress', 'Security Details', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'LOI In Progress', 'Security Deposit', 'Security Details', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack',
'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion',
'Statutory GST', 'Statutory PAN', 'Statutory Nodal', 'Statutory Check', 'Statutory GST', 'Statutory PAN', 'Statutory Nodal', 'Statutory Check',
'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental',
@ -198,7 +198,7 @@ export function useApplicationDetailsPermissions({
const isLoaLocked = application.status === 'LOA Pending' && getDeposit('FIRST_FILL')?.status !== 'Verified'; const isLoaLocked = application.status === 'LOA Pending' && getDeposit('FIRST_FILL')?.status !== 'Verified';
const isSecurityDetailsLocked = const isSecurityDetailsLocked =
['Security Details', 'Payment Pending'].includes(application.status) && ['Security Deposit', 'Security Details', 'Payment Pending'].includes(application.status) &&
getDeposit('SECURITY_DEPOSIT')?.status !== 'Verified'; getDeposit('SECURITY_DEPOSIT')?.status !== 'Verified';
const isFinalState = application.status === 'Onboarded' || application.status === 'Rejected'; const isFinalState = application.status === 'Onboarded' || application.status === 'Rejected';

View File

@ -40,6 +40,14 @@ export function useApplicationDetailsStageData({
return (documents || []).some((d) => d.documentType === docType); return (documents || []).some((d) => d.documentType === docType);
}; };
const getSecurityDepositStageStatus = (): ProcessStage['status'] => {
const rows = application.progressTracking || [];
const row =
rows.find((ps: any) => ps.stageName === 'Security Deposit') ||
rows.find((ps: any) => ps.stageName === 'Security Details');
return row?.status ? (row.status as ProcessStage['status']) : 'pending';
};
const getStageStatus = (stageName: string, fallbackStatus: ProcessStage['status'] = 'pending'): ProcessStage['status'] => { const getStageStatus = (stageName: string, fallbackStatus: ProcessStage['status'] = 'pending'): ProcessStage['status'] => {
const backendStage = (application.progressTracking || []).find((ps: any) => ps.stageName === stageName); const backendStage = (application.progressTracking || []).find((ps: any) => ps.stageName === stageName);
return backendStage?.status ? (backendStage.status as any) : fallbackStatus; return backendStage?.status ? (backendStage.status as any) : fallbackStatus;
@ -113,8 +121,8 @@ export function useApplicationDetailsStageData({
documentsUploaded: 1 documentsUploaded: 1
}, },
{ {
id: 9, name: 'Security Details', status: getStageStatus('Security Details'), id: 9, name: 'Security Deposit', status: getSecurityDepositStageStatus(),
date: application.securityDetailsDate, description: 'Security verification', documentsUploaded: 3 date: application.securityDetailsDate, description: 'Security Deposit verification', documentsUploaded: 3
}, },
{ {
id: 10, name: 'LOI Issue', status: getStageStatus('LOI Issue'), id: 10, name: 'LOI Issue', status: getStageStatus('LOI Issue'),

View File

@ -242,6 +242,10 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
'Disqualified': 'bg-gray-100 text-gray-800', 'Disqualified': 'bg-gray-100 text-gray-800',
'Onboarded': 'bg-emerald-100 text-emerald-800', 'Onboarded': 'bg-emerald-100 text-emerald-800',
'LOI Approved': 'bg-sky-100 text-sky-800', 'LOI Approved': 'bg-sky-100 text-sky-800',
'Security Deposit In Progress': 'bg-amber-100 text-amber-800',
'Security Deposit Approved': 'bg-green-100 text-green-800',
'Security Deposit': 'bg-amber-100 text-amber-800',
/** Legacy overallStatus until DB migrated */
'Security Details In Progress': 'bg-amber-100 text-amber-800', 'Security Details In Progress': 'bg-amber-100 text-amber-800',
'Security Details Approved': 'bg-green-100 text-green-800', 'Security Details Approved': 'bg-green-100 text-green-800',
'Security Details': 'bg-amber-100 text-amber-800', 'Security Details': 'bg-amber-100 text-amber-800',

View File

@ -273,14 +273,6 @@ export function ApplicationsPage({ onViewDetails, initialFilter }: ApplicationsP
<Label htmlFor="my-assignments" data-testid="onboarding-applications-assignments-label">My Assignments Only</Label> <Label htmlFor="my-assignments" data-testid="onboarding-applications-assignments-label">My Assignments Only</Label>
</div> </div>
<Select value={sortBy} onValueChange={(v) => setSortBy(v as any)}>
<SelectTrigger className="w-full lg:w-40" data-testid="onboarding-applications-sort-select">
<SelectValue placeholder="Sort By" />
</SelectTrigger>
<SelectContent>
<SelectItem value="date">Date</SelectItem>
</SelectContent>
</Select>
</div> </div>
<div className="flex flex-wrap items-center gap-3 mt-4"> <div className="flex flex-wrap items-center gap-3 mt-4">

View File

@ -55,7 +55,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardin
const paymentRows = applications.flatMap((app: any) => { const paymentRows = applications.flatMap((app: any) => {
const s = app.overallStatus || app.status; const s = app.overallStatus || app.status;
const isPaymentStage = [ const isPaymentStage = [
'Payment Pending', 'Security Details', 'LOI In Progress', 'LOI Issued', 'Payment Pending', 'Security Deposit', 'Security Details', 'LOI In Progress', 'LOI Issued',
'LOA Pending', 'Dealer Code Generation', 'LOA_APPROVAL' 'LOA Pending', 'Dealer Code Generation', 'LOA_APPROVAL'
].includes(s); ].includes(s);
const deposits = app.securityDeposits || []; const deposits = app.securityDeposits || [];
@ -77,7 +77,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardin
// Keep virtual pending rows for in-flight cases with no deposit record yet // Keep virtual pending rows for in-flight cases with no deposit record yet
if (isPaymentStage) { if (isPaymentStage) {
if (['Payment Pending', 'Security Details', 'LOI In Progress'].includes(s)) { if (['Payment Pending', 'Security Deposit', 'Security Details', 'LOI In Progress'].includes(s)) {
return [{ return [{
id: `virtual-${app.id}-sd`, id: `virtual-${app.id}-sd`,
applicationId: app.applicationId || app.id, applicationId: app.applicationId || app.id,
@ -134,7 +134,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardin
<div className="flex items-center justify-between bg-white p-6 rounded-2xl border border-slate-100 shadow-sm"> <div className="flex items-center justify-between bg-white p-6 rounded-2xl border border-slate-100 shadow-sm">
<div> <div>
<h1 className="text-3xl font-bold text-slate-900 tracking-tight mb-1">Payment Verification</h1> <h1 className="text-3xl font-bold text-slate-900 tracking-tight mb-1">Payment Verification</h1>
<p className="text-slate-500">Review and verify dealer security deposits and first fill payments</p> <p className="text-slate-500">Review and verify Security Deposit and First Fill payments for dealers</p>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Button onClick={fetchApplications} variant="outline" size="sm" className="bg-white hover:bg-slate-50" data-testid="onboarding-finance-queue-sync-btn"> <Button onClick={fetchApplications} variant="outline" size="sm" className="bg-white hover:bg-slate-50" data-testid="onboarding-finance-queue-sync-btn">

View File

@ -378,6 +378,9 @@ export function OpportunityRequestsPage({ onViewDetails }: OpportunityRequestsPa
'Level 3 Approved': 'bg-green-100 text-green-800', 'Level 3 Approved': 'bg-green-100 text-green-800',
'LOI In Progress': 'bg-sky-50 text-sky-700', 'LOI In Progress': 'bg-sky-50 text-sky-700',
'LOI Approved': 'bg-green-100 text-green-800', 'LOI Approved': 'bg-green-100 text-green-800',
'Security Deposit In Progress': 'bg-blue-50 text-blue-700',
'Security Deposit Approved': 'bg-green-100 text-green-800',
'Security Deposit': 'bg-blue-100 text-blue-800',
'Security Details In Progress': 'bg-blue-50 text-blue-700', 'Security Details In Progress': 'bg-blue-50 text-blue-700',
'Security Details Approved': 'bg-green-100 text-green-800', 'Security Details Approved': 'bg-green-100 text-green-800',
'Security Details': 'bg-blue-100 text-blue-800', 'Security Details': 'bg-blue-100 text-blue-800',

View File

@ -46,6 +46,10 @@ export type ApplicationStatus =
| 'Payment Pending' | 'Payment Pending'
| 'LOI In Progress' | 'LOI In Progress'
| 'LOI Approved' | 'LOI Approved'
| 'Security Deposit In Progress'
| 'Security Deposit Approved'
| 'Security Deposit'
/** Legacy overallStatus from API / DB before rename */
| 'Security Details In Progress' | 'Security Details In Progress'
| 'Security Details Approved' | 'Security Details Approved'
| 'Security Details' | 'Security Details'

View File

@ -132,12 +132,12 @@ export const onboardingService = {
}, },
getSecurityDeposit: async (applicationId: string) => { getSecurityDeposit: async (applicationId: string) => {
const response: any = await API.getSecurityDeposit(applicationId); const response: any = await API.getSecurityDeposit(applicationId);
if (!response.ok) throw new Error(response.data?.message || 'Failed to fetch security deposit'); if (!response.ok) throw new Error(response.data?.message || 'Failed to fetch Security Deposit');
return response.data?.data || response.data; return response.data?.data || response.data;
}, },
updateSecurityDeposit: async (data: any) => { updateSecurityDeposit: async (data: any) => {
const response: any = await API.updateSecurityDeposit(data); const response: any = await API.updateSecurityDeposit(data);
if (!response.ok) throw new Error(response.data?.message || 'Failed to update security deposit'); if (!response.ok) throw new Error(response.data?.message || 'Failed to update Security Deposit');
return response.data; return response.data;
}, },
getSystemConfigs: async (params?: any) => { getSystemConfigs: async (params?: any) => {