feat(loan-origination): add full Loan Origination Sanctioning workflow module
New standalone module modelling the pre-disbursement pipeline: Channel intake (Branch/BC/Self-Apply) -> KYC/OVD & e-KYC fingerprint -> CIC/Credit Bureau check -> Field Investigation -> Personal Discussion & declaration -> Loan Committee -> Risk Approval -> Legal Approval -> Customer Signing -> Cheque/ACH -> Post-Disbursement Check -> Disbursement, per RBI KYC Master Direction, the mandatory CIC check, and co-operative-bank share-membership rules. Includes a 12-stage stepper with real approve/reject actions and a date-wise audit trail, CIC upload + credit-score check, Field Inquiry push + Agent Report upload, Committee "Reports Required" / Risk "Verification Checklist", Head-Office-vs-branch scoping, a Pipeline screen (filters, New Application intake, Channel-wise/BC-wise/Share-Member reports, Disbursement Target vs Actual, Export to Excel), and a full EMI repayment schedule once disbursed.
This commit is contained in:
@@ -0,0 +1,878 @@
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="pg">
|
||||
@if (application; as a) {
|
||||
<header class="pg-head">
|
||||
<div>
|
||||
<div class="pg-eyebrow">CREDIT · LOAN ORIGINATION · {{ a.branchName | uppercase }}</div>
|
||||
<h1 class="pg-title">
|
||||
{{ a.applicationNo }} — {{ a.applicantName }}
|
||||
<span [class]="badgeClass(a)">{{ statusLabels[a.overallStatus] }}</span>
|
||||
</h1>
|
||||
<p class="pg-sub npa-report-note">
|
||||
{{ a.productName }} · ₹{{ a.requestedAmount | number: '1.0-0' }} · Applied {{ a.applicationDate }} ·
|
||||
{{ progress(a) }}% through the sanctioning workflow.
|
||||
</p>
|
||||
</div>
|
||||
<div class="pg-toolbar">
|
||||
<button type="button" class="pg-btn pg-btn--ghost" (click)="goBack()">‹ Back to Pipeline</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="pg-card">
|
||||
<dl class="loan-kv-grid">
|
||||
<div class="loan-kv-field">
|
||||
<dt>Branch</dt>
|
||||
<dd>{{ a.branchName }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Channel</dt>
|
||||
<dd>
|
||||
{{ a.channel === 'BC' ? a.bcAgentName : a.channel === 'SELF_APPLY' ? 'Self-Apply (Digital)' : 'Branch' }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Customer Type</dt>
|
||||
<dd>{{ a.customerType === 'EXISTING' ? 'Existing Customer' : 'New Customer' }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Tenure / Rate</dt>
|
||||
<dd>{{ a.tenureMonths }} months @ {{ a.interestRate }}%</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Monthly Income / Eligible Amount</dt>
|
||||
<dd>₹{{ a.monthlyIncome | number: '1.0-0' }} / ₹{{ a.eligibleLoanAmount | number: '1.0-0' }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Sanctioning Authority</dt>
|
||||
<dd>{{ sanctioningAuthorityOf(a) }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Share Member</dt>
|
||||
<dd>
|
||||
@if (a.isShareMember) {
|
||||
Yes — {{ a.shareCertNo }} (since {{ a.shareMemberSince }})
|
||||
} @else {
|
||||
<span class="loan-badge loan-badge--hold">Not a Member</span>
|
||||
<button type="button" class="pg-btn pg-btn--ghost" (click)="allotMembership(a)">Allot Shares</button>
|
||||
}
|
||||
</dd>
|
||||
</div>
|
||||
@if (a.coBorrowerRequired) {
|
||||
<div class="loan-kv-field">
|
||||
<dt>Co-Borrower</dt>
|
||||
<dd>{{ a.coBorrowerName }}</dd>
|
||||
</div>
|
||||
}
|
||||
@if (a.guarantorRequired) {
|
||||
<div class="loan-kv-field">
|
||||
<dt>Guarantor</dt>
|
||||
<dd>{{ a.guarantorName }}</dd>
|
||||
</div>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Sanctioning Workflow Progress</div>
|
||||
<div class="loan-stepper">
|
||||
@for (s of stageOrder; track s) {
|
||||
<div [class]="stepStatusClass(a, s)">
|
||||
<span class="loan-step__connector"></span>
|
||||
<span class="loan-step__dot">{{ stepMarker(a, s) }}</span>
|
||||
<span class="loan-step__label">{{ stageLabels[s] }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (a.overallStatus === 'REJECTED') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Application Rejected — at {{ stageLabels[a.currentStage] }}</div>
|
||||
<p class="pg-sub npa-report-note">See the approval summary below for the recorded reason.</p>
|
||||
</div>
|
||||
} @else if (a.overallStatus === 'ON_HOLD') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Personal Discussion — {{ discrepancyLabels[a.discrepancyType] }}</div>
|
||||
<div class="loan-action-card">
|
||||
<p>{{ a.personalDiscussionNotes }}</p>
|
||||
<label class="inv-form-field">
|
||||
<span>Resolution Notes</span>
|
||||
<input type="text" [(ngModel)]="actionRemarks" placeholder="How was this resolved?" />
|
||||
</label>
|
||||
<div class="loan-action-row">
|
||||
<button type="button" class="pg-btn pg-btn--success" (click)="resolveDiscrepancy(a)">
|
||||
Resolve & Proceed to Loan Committee
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @else if (a.overallStatus === 'DISBURSED') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Disbursed</div>
|
||||
<dl class="loan-kv-grid">
|
||||
<div class="loan-kv-field">
|
||||
<dt>Disbursement Date</dt>
|
||||
<dd>{{ a.disbursementDate }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Mode</dt>
|
||||
<dd>{{ a.disbursementMode }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>ACH Confirmation</dt>
|
||||
<dd>{{ a.achConfirmationRef }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>EMI Activation Confirmation</dt>
|
||||
<dd>{{ a.emiActivationConfirmed ? 'Confirmed on ' + a.emiActivationDate : 'Pending' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">EMI Repayment Schedule</div>
|
||||
@if (isRevolving(a)) {
|
||||
<p class="pg-sub npa-report-note">
|
||||
{{ a.productName }} is a revolving/renewable limit — repaid on demand and renewed annually, not on a fixed
|
||||
EMI schedule.
|
||||
</p>
|
||||
} @else {
|
||||
<div class="inv-kpis npa-kpis--ratio">
|
||||
<div class="inv-kpi">
|
||||
<span class="inv-kpi__label">Loan Amount</span>
|
||||
<span class="inv-kpi__value">₹{{ a.requestedAmount | number: '1.0-0' }}</span>
|
||||
<span class="inv-kpi__sub">{{ a.tenureMonths }} months @ {{ a.interestRate }}%</span>
|
||||
</div>
|
||||
<div class="inv-kpi">
|
||||
<span class="inv-kpi__label">Monthly EMI</span>
|
||||
<span class="inv-kpi__value">₹{{ emiAmountFor(a) | number: '1.0-0' }}</span>
|
||||
<span class="inv-kpi__sub">Reducing balance</span>
|
||||
</div>
|
||||
<div class="inv-kpi">
|
||||
<span class="inv-kpi__label">Total Interest Payable</span>
|
||||
<span class="inv-kpi__value">₹{{ totalInterestFor(a) | number: '1.0-0' }}</span>
|
||||
<span class="inv-kpi__sub">over the full tenure</span>
|
||||
</div>
|
||||
<div class="inv-kpi">
|
||||
<span class="inv-kpi__label">Total Amount Payable</span>
|
||||
<span class="inv-kpi__value">₹{{ totalPayableFor(a) | number: '1.0-0' }}</span>
|
||||
<span class="inv-kpi__sub">principal + interest</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pg-table-scroll loan-emi-table-scroll">
|
||||
<table class="pg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="num">Inst. No</th>
|
||||
<th>Due Date</th>
|
||||
<th class="num">Opening Balance</th>
|
||||
<th class="num">EMI</th>
|
||||
<th class="num">Principal</th>
|
||||
<th class="num col-out">Interest</th>
|
||||
<th class="num">Closing Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (row of emiScheduleFor(a); track row.installmentNo) {
|
||||
<tr>
|
||||
<td class="num">{{ row.installmentNo }}</td>
|
||||
<td>{{ row.dueDate }}</td>
|
||||
<td class="num">₹{{ row.openingBalance | number: '1.0-0' }}</td>
|
||||
<td class="num">₹{{ row.emiAmount | number: '1.0-0' }}</td>
|
||||
<td class="num col-in">₹{{ row.principalComponent | number: '1.0-0' }}</td>
|
||||
<td class="num col-out">₹{{ row.interestComponent | number: '1.0-0' }}</td>
|
||||
<td class="num">₹{{ row.closingBalance | number: '1.0-0' }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} @else {
|
||||
<!-- ===================== APPLICATION ===================== -->
|
||||
@if (a.currentStage === 'APPLICATION') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Application Received</div>
|
||||
<div class="loan-action-card">
|
||||
<p>
|
||||
Received via
|
||||
{{ a.channel === 'BC' ? a.bcAgentName : a.channel === 'SELF_APPLY' ? 'Self-Apply (Digital)' : 'Branch' }}
|
||||
on {{ a.applicationDate }}.
|
||||
</p>
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--primary"
|
||||
(click)="proceed(a, 'Application logged and forwarded for KYC.')"
|
||||
>
|
||||
Proceed to KYC & Document Collection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== KYC & DOCUMENTS ===================== -->
|
||||
@if (a.currentStage === 'KYC_DOCUMENTS') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">KYC & Document Collection — OVD (RBI KYC Master Direction)</div>
|
||||
<div class="loan-action-card">
|
||||
<div class="loan-checklist">
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.aadhaarVerified" /> Aadhaar Verified (OVD)
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.panVerified" /> PAN Verified
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.incomeProofSubmitted" /> Income Proof Submitted
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.bankStatementSubmitted" /> Bank Statement Submitted
|
||||
</label>
|
||||
@if (a.secured) {
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.propertyDetailsSubmitted" /> Property Details Submitted
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.taxReceiptSubmitted" /> Property Tax Receipt Submitted
|
||||
</label>
|
||||
}
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.electricityBillSubmitted" /> Electricity Bill (Address Proof)
|
||||
Submitted
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.aadhaarFingerprintCaptured" /> Aadhaar Fingerprint Captured
|
||||
(e-KYC)
|
||||
</label>
|
||||
</div>
|
||||
@if (a.monthlyIncome === 0) {
|
||||
<label class="inv-form-field">
|
||||
<span>Monthly Income (₹)</span>
|
||||
<input type="number" [(ngModel)]="monthlyIncomeInput" placeholder="e.g. 45000" />
|
||||
</label>
|
||||
<p class="pg-sub npa-report-note">
|
||||
Eligible Loan Amount preview: ₹{{ eligiblePreview(a) | number: '1.0-0' }}
|
||||
</p>
|
||||
<div class="loan-action-row">
|
||||
<button type="button" class="pg-btn pg-btn--ghost" (click)="setIncomeNow(a)">
|
||||
Assess Income & Calculate Eligible Amount
|
||||
</button>
|
||||
</div>
|
||||
} @else {
|
||||
<dl class="loan-kv-grid" style="padding: 0 0 14px">
|
||||
<div class="loan-kv-field">
|
||||
<dt>Monthly Income</dt>
|
||||
<dd>₹{{ a.monthlyIncome | number: '1.0-0' }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Eligible Loan Amount</dt>
|
||||
<dd>₹{{ a.eligibleLoanAmount | number: '1.0-0' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
}
|
||||
@if (a.coBorrowerRequired) {
|
||||
<p class="pg-sub npa-report-note">Co-Borrower Required — {{ a.coBorrowerName }}</p>
|
||||
}
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--primary"
|
||||
[disabled]="!allKycDone(a)"
|
||||
(click)="proceed(a, 'KYC/OVD documents verified and collected.')"
|
||||
>
|
||||
Proceed to CIC / Credit Bureau Check
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== CIC / CREDIT BUREAU CHECK ===================== -->
|
||||
@if (a.currentStage === 'CIC_CHECK') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">CIC / Credit Bureau Check — {{ a.cicBureau }}</div>
|
||||
<div class="loan-action-card">
|
||||
@if (a.cibilScore === 0) {
|
||||
<label class="inv-form-field">
|
||||
<span>Bureau</span>
|
||||
<select [(ngModel)]="a.cicBureau">
|
||||
@for (bureau of cicBureaus; track bureau) {
|
||||
<option [value]="bureau">{{ bureau }}</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
<div class="loan-action-row">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="checkCreditScoreNow(a)">
|
||||
Check Credit Score
|
||||
</button>
|
||||
</div>
|
||||
} @else {
|
||||
<dl class="loan-kv-grid" style="padding: 0 0 14px">
|
||||
<div class="loan-kv-field">
|
||||
<dt>Bureau</dt>
|
||||
<dd>{{ a.cicBureau }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Credit Score</dt>
|
||||
<dd>{{ a.cibilScore }} ({{ cibilBandOf(a.cibilScore) }})</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Report</dt>
|
||||
<dd>{{ a.cicReportUploaded ? a.cicReportFileName : 'Not uploaded yet' }}</dd>
|
||||
</div>
|
||||
<div class="loan-kv-field">
|
||||
<dt>Field Inquiry</dt>
|
||||
<dd>
|
||||
{{
|
||||
a.fiRequestPushed
|
||||
? 'Pushed to ' + a.fiAgencyAssigned + ' (' + a.fiRequestDate + ')'
|
||||
: 'Not pushed yet'
|
||||
}}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
}
|
||||
@if (a.cibilScore > 0) {
|
||||
@if (!a.cicReportUploaded) {
|
||||
<div class="loan-action-row">
|
||||
<label class="pg-btn pg-btn--ghost" style="cursor: pointer">
|
||||
Upload {{ a.cicBureau }} Report
|
||||
<input type="file" hidden accept=".pdf,.jpg,.jpeg,.png" (change)="onCicFileSelected($event, a)" />
|
||||
</label>
|
||||
</div>
|
||||
} @else if (!a.fiRequestPushed) {
|
||||
<div class="loan-action-row">
|
||||
<button type="button" class="pg-btn pg-btn--ghost" (click)="pushFieldInquiryNow(a)">
|
||||
Push Field Inquiry to Agency
|
||||
</button>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--primary"
|
||||
(click)="
|
||||
proceed(
|
||||
a,
|
||||
a.cicBureau +
|
||||
' report reviewed — score ' +
|
||||
a.cibilScore +
|
||||
' (' +
|
||||
cibilBandOf(a.cibilScore) +
|
||||
').'
|
||||
)
|
||||
"
|
||||
>
|
||||
Proceed to Field Investigation
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== FIELD INVESTIGATION ===================== -->
|
||||
@if (a.currentStage === 'FIELD_INVESTIGATION') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Field Investigation (FI) — Agent Report</div>
|
||||
<div class="loan-action-card">
|
||||
<div class="inv-form-grid">
|
||||
<label class="inv-form-field">
|
||||
<span>Address Verification</span>
|
||||
<select [(ngModel)]="a.addressVerification.status">
|
||||
<option value="PENDING">Pending</option>
|
||||
<option value="VERIFIED">Verified</option>
|
||||
<option value="FAILED">Failed</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="inv-form-field">
|
||||
<span>Office / Employment Verification</span>
|
||||
<select [(ngModel)]="a.officeVerification.status">
|
||||
<option value="PENDING">Pending</option>
|
||||
<option value="VERIFIED">Verified</option>
|
||||
<option value="FAILED">Failed</option>
|
||||
</select>
|
||||
</label>
|
||||
@if (a.secured) {
|
||||
<label class="inv-form-field">
|
||||
<span>Property Verification / Valuation</span>
|
||||
<select [(ngModel)]="a.propertyVerification.status">
|
||||
<option value="PENDING">Pending</option>
|
||||
<option value="VERIFIED">Verified</option>
|
||||
<option value="FAILED">Failed</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="inv-form-field">
|
||||
<span>Property Valuation (₹)</span>
|
||||
<input type="number" [(ngModel)]="a.propertyValuation" />
|
||||
</label>
|
||||
}
|
||||
</div>
|
||||
<div class="loan-kv-grid" style="padding: 0 0 14px">
|
||||
<div class="loan-kv-field">
|
||||
<dt>Agent Report</dt>
|
||||
<dd>{{ a.agentReportUploaded ? a.agentReportFileName : 'Not uploaded yet' }}</dd>
|
||||
</div>
|
||||
</div>
|
||||
@if (!a.agentReportUploaded) {
|
||||
<div class="loan-action-row">
|
||||
<label class="pg-btn pg-btn--ghost" style="cursor: pointer">
|
||||
Upload Agent Report
|
||||
<input
|
||||
type="file"
|
||||
hidden
|
||||
accept=".pdf,.jpg,.jpeg,.png"
|
||||
(change)="onAgentReportFileSelected($event, a)"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--primary"
|
||||
[disabled]="!fiComplete(a) || !a.agentReportUploaded"
|
||||
(click)="completeFi(a)"
|
||||
>
|
||||
Record FI & Proceed to Personal Discussion
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== PERSONAL DISCUSSION ===================== -->
|
||||
@if (a.currentStage === 'PERSONAL_DISCUSSION') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Personal Discussion with Manager</div>
|
||||
<div class="loan-action-card">
|
||||
<label class="inv-form-field">
|
||||
<span>Discrepancy (if any)</span>
|
||||
<select [(ngModel)]="discrepancySelection">
|
||||
<option value="NONE">No Discrepancy</option>
|
||||
<option value="DOCUMENT_DISCREPANCY">Document Discrepancy</option>
|
||||
<option value="NAME_MISMATCH">Name Mismatch</option>
|
||||
<option value="OFFICE_VERIFICATION_FAILED">Office Verification Failed</option>
|
||||
<option value="PROPERTY_DISCREPANCY">Property Discrepancy</option>
|
||||
<option value="FI_REPORT_DISCREPANCY">FI Report Discrepancy</option>
|
||||
<option value="FAKE_DECLARATION">Fake Declaration Suspected</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="inv-form-field">
|
||||
<span>Discussion Notes</span>
|
||||
<input type="text" [(ngModel)]="actionRemarks" placeholder="Notes from discussion with manager" />
|
||||
</label>
|
||||
<div class="loan-action-row">
|
||||
@if (discrepancySelection === 'NONE') {
|
||||
<button type="button" class="pg-btn pg-btn--success" (click)="clearPersonalDiscussion(a)">
|
||||
Clear, Take Declaration & Proceed to Loan Committee
|
||||
</button>
|
||||
} @else {
|
||||
<button type="button" class="pg-btn pg-btn--warn" (click)="flagPersonalDiscussion(a)">
|
||||
Flag Discrepancy — Hold Application
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== LOAN COMMITTEE APPROVAL ===================== -->
|
||||
@if (a.currentStage === 'CREDIT_COMMITTEE') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Loan Committee Approval — {{ sanctioningAuthorityOf(a) }}</div>
|
||||
<div class="loan-action-card">
|
||||
<p class="pg-sub npa-report-note">Reports Required — present below reports, then complete.</p>
|
||||
<div class="loan-checklist">
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.cicReportUploaded" disabled /> CIBIL / Credit Bureau Report
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.backgroundCheckDone" /> Background Check
|
||||
</label>
|
||||
@if (a.secured) {
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.cersaiSearchDone" /> CSE — CERSAI Search Enquiry
|
||||
</label>
|
||||
}
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.aadhaarVerified && a.panVerified" disabled /> OVD
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.incomeProofSubmitted" disabled /> Income Proof
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="fiComplete(a)" disabled /> FI Report
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.managerMeetingDone" /> Manager Meeting
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.isShareMember" disabled /> Share Member Approval
|
||||
</label>
|
||||
@if (a.secured) {
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.propertyValuation > 0" disabled /> Property Valuation
|
||||
</label>
|
||||
}
|
||||
</div>
|
||||
<label class="inv-form-field">
|
||||
<span>Committee Remarks</span>
|
||||
<input type="text" [(ngModel)]="actionRemarks" placeholder="Remarks" />
|
||||
</label>
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--success"
|
||||
[disabled]="!committeeReportsReady(a)"
|
||||
(click)="approveStage(a, 'creditCommitteeApproval', 'Sanctioning Authority')"
|
||||
>
|
||||
Approve
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--danger"
|
||||
(click)="rejectStage(a, 'creditCommitteeApproval', 'Sanctioning Authority')"
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== RISK APPROVAL ===================== -->
|
||||
@if (a.currentStage === 'RISK_APPROVAL') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Risk Approval — Verification Checklist</div>
|
||||
<div class="loan-action-card">
|
||||
<div class="loan-checklist">
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.addressVerification.status === 'VERIFIED'" disabled /> ID / Address
|
||||
Verified
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.incomeProofSubmitted" disabled /> Income Proof Verified
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.previousLoanDeclarationDone" /> Previous Loan Declaration
|
||||
</label>
|
||||
@if (a.secured) {
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.propertySearchReportDone" /> Property Search Report
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.valuationReportConfirmed" /> Valuation Report
|
||||
</label>
|
||||
}
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="fiComplete(a)" disabled /> FI Report
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [ngModel]="a.backgroundCheckDone" disabled /> Personal Background Check
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.guarantorRequired" /> Guarantor Required
|
||||
</label>
|
||||
</div>
|
||||
@if (a.guarantorRequired) {
|
||||
<label class="inv-form-field">
|
||||
<span>Guarantor Name</span>
|
||||
<input type="text" [(ngModel)]="a.guarantorName" />
|
||||
</label>
|
||||
}
|
||||
<label class="inv-form-field">
|
||||
<span>Risk Remarks</span>
|
||||
<input
|
||||
type="text"
|
||||
[(ngModel)]="actionRemarks"
|
||||
placeholder="ID/address, income adequacy, previous-loan declaration"
|
||||
/>
|
||||
</label>
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--success"
|
||||
[disabled]="!riskChecklistReady(a)"
|
||||
(click)="approveStage(a, 'riskApproval', 'Risk Approval')"
|
||||
>
|
||||
Approve
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--danger"
|
||||
(click)="rejectStage(a, 'riskApproval', 'Risk Approval')"
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== LEGAL APPROVAL ===================== -->
|
||||
@if (a.currentStage === 'LEGAL_APPROVAL') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Legal Approval — Fees & A/c Opened By</div>
|
||||
<div class="loan-action-card">
|
||||
<div class="loan-checklist">
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.feesAndAccountOpened" /> Fees & A/c Opening Done
|
||||
</label>
|
||||
@if (a.secured) {
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.mortgageCreated" /> Mortgage Created
|
||||
</label>
|
||||
}
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.legalStampDone" /> Legal Stamp Done
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.fileFilingDone" /> File Filing Done
|
||||
</label>
|
||||
</div>
|
||||
<label class="inv-form-field">
|
||||
<span>Agreement Signing Slot</span>
|
||||
<input type="text" [(ngModel)]="a.agreementSigningSlot" placeholder="DD-MM-YYYY" />
|
||||
</label>
|
||||
<label class="inv-form-field">
|
||||
<span>Legal Remarks</span>
|
||||
<input type="text" [(ngModel)]="actionRemarks" placeholder="Remarks" />
|
||||
</label>
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--success"
|
||||
[disabled]="!legalChecklistDone(a)"
|
||||
(click)="approveStage(a, 'legalApproval', 'Legal Approval')"
|
||||
>
|
||||
Approve & Allot Signing Slot
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--danger"
|
||||
(click)="rejectStage(a, 'legalApproval', 'Legal Approval')"
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== CUSTOMER SIGNING SLOT ===================== -->
|
||||
@if (a.currentStage === 'CUSTOMER_SIGNING') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Customer Signing Slot — {{ a.agreementSigningSlot }}</div>
|
||||
<div class="loan-action-card">
|
||||
<p>Loan Agreement and Sanction Letter ready for the customer's acknowledgement and signature.</p>
|
||||
<div class="loan-action-row">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="markSigned(a)">
|
||||
Mark Signed by Customer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== CHEQUE RECEIVED / ACH SIGN ===================== -->
|
||||
@if (a.currentStage === 'CHEQUE_ACH') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Cheque Received / ACH Sign</div>
|
||||
<div class="loan-action-card">
|
||||
<div class="loan-checklist">
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.chequeReceived" /> Cheque Received
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.achSigned" /> ACH Mandate Signed
|
||||
</label>
|
||||
</div>
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--primary"
|
||||
[disabled]="!a.chequeReceived || !a.achSigned"
|
||||
(click)="confirmChequeAndProceed(a)"
|
||||
>
|
||||
Confirm & Proceed to Post-Disbursement Check
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== POST-DISBURSEMENT CHECK ===================== -->
|
||||
@if (a.currentStage === 'POST_CHECK') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Post-Disbursement Check — Final Compliance Gate</div>
|
||||
<div class="loan-action-card">
|
||||
<p class="pg-sub npa-report-note">Document Signing by:</p>
|
||||
<div class="loan-checklist">
|
||||
<label class="loan-checklist-item"> <input type="checkbox" [(ngModel)]="a.signedByAgent" /> Agent </label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.signedByManager" /> Manager
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.signedByCommittee" /> Committee
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.signedByRiskApproval" /> Risk Approval
|
||||
</label>
|
||||
<label class="loan-checklist-item">
|
||||
<input type="checkbox" [(ngModel)]="a.signedByLegalApproval" /> Legal Approval
|
||||
</label>
|
||||
</div>
|
||||
<label class="inv-form-field">
|
||||
<span>Remarks</span>
|
||||
<input type="text" [(ngModel)]="actionRemarks" placeholder="Remarks" />
|
||||
</label>
|
||||
<div class="loan-action-row">
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--success"
|
||||
[disabled]="!postCheckSignaturesDone(a)"
|
||||
(click)="approveStage(a, 'postCheckApproval', 'Post-Disbursement Check')"
|
||||
>
|
||||
Clear for Disbursement
|
||||
</button>
|
||||
<button type="button" class="pg-btn pg-btn--warn" (click)="exceptionApprove(a)">
|
||||
Approve on Exception
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pg-btn pg-btn--danger"
|
||||
(click)="rejectStage(a, 'postCheckApproval', 'Post-Disbursement Check')"
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ===================== DISBURSEMENT ===================== -->
|
||||
@if (a.currentStage === 'DISBURSED') {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Ready for Disbursement</div>
|
||||
<div class="loan-action-card">
|
||||
<label class="inv-form-field">
|
||||
<span>Disbursement Mode</span>
|
||||
<select [(ngModel)]="disbursementMode">
|
||||
<option value="Account Credit">Account Credit</option>
|
||||
<option value="NEFT">NEFT</option>
|
||||
<option value="RTGS">RTGS</option>
|
||||
</select>
|
||||
</label>
|
||||
<p class="pg-sub npa-report-note">
|
||||
Disbursing also confirms EMI Activation for this account in the same step.
|
||||
</p>
|
||||
<div class="loan-action-row">
|
||||
<button type="button" class="pg-btn pg-btn--success" (click)="disburseNow(a)">
|
||||
Disburse Now & Confirm EMI Activation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Approval Summary</div>
|
||||
<div class="loan-approval-summary">
|
||||
<div
|
||||
class="loan-approval-tile"
|
||||
[class.loan-approval-tile--approved]="
|
||||
a.creditCommitteeApproval.decision === 'APPROVED' ||
|
||||
a.creditCommitteeApproval.decision === 'EXCEPTION_APPROVED'
|
||||
"
|
||||
[class.loan-approval-tile--rejected]="a.creditCommitteeApproval.decision === 'REJECTED'"
|
||||
[class.loan-approval-tile--pending]="a.creditCommitteeApproval.decision === 'PENDING'"
|
||||
>
|
||||
<span class="loan-approval-tile__role">Loan Committee</span>
|
||||
<span class="loan-approval-tile__decision">{{ a.creditCommitteeApproval.decision }}</span>
|
||||
<span>{{ a.creditCommitteeApproval.approverName }} · {{ a.creditCommitteeApproval.date }}</span>
|
||||
<span>{{ a.creditCommitteeApproval.remarks }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="loan-approval-tile"
|
||||
[class.loan-approval-tile--approved]="
|
||||
a.riskApproval.decision === 'APPROVED' || a.riskApproval.decision === 'EXCEPTION_APPROVED'
|
||||
"
|
||||
[class.loan-approval-tile--rejected]="a.riskApproval.decision === 'REJECTED'"
|
||||
[class.loan-approval-tile--pending]="a.riskApproval.decision === 'PENDING'"
|
||||
>
|
||||
<span class="loan-approval-tile__role">Risk Approval</span>
|
||||
<span class="loan-approval-tile__decision">{{ a.riskApproval.decision }}</span>
|
||||
<span>{{ a.riskApproval.approverName }} · {{ a.riskApproval.date }}</span>
|
||||
<span>{{ a.riskApproval.remarks }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="loan-approval-tile"
|
||||
[class.loan-approval-tile--approved]="
|
||||
a.legalApproval.decision === 'APPROVED' || a.legalApproval.decision === 'EXCEPTION_APPROVED'
|
||||
"
|
||||
[class.loan-approval-tile--rejected]="a.legalApproval.decision === 'REJECTED'"
|
||||
[class.loan-approval-tile--pending]="a.legalApproval.decision === 'PENDING'"
|
||||
>
|
||||
<span class="loan-approval-tile__role">Legal Approval</span>
|
||||
<span class="loan-approval-tile__decision">{{ a.legalApproval.decision }}</span>
|
||||
<span>{{ a.legalApproval.approverName }} · {{ a.legalApproval.date }}</span>
|
||||
<span>{{ a.legalApproval.remarks }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="loan-approval-tile"
|
||||
[class.loan-approval-tile--approved]="
|
||||
a.postCheckApproval.decision === 'APPROVED' || a.postCheckApproval.decision === 'EXCEPTION_APPROVED'
|
||||
"
|
||||
[class.loan-approval-tile--rejected]="a.postCheckApproval.decision === 'REJECTED'"
|
||||
[class.loan-approval-tile--pending]="a.postCheckApproval.decision === 'PENDING'"
|
||||
>
|
||||
<span class="loan-approval-tile__role">Post-Disbursement Check</span>
|
||||
<span class="loan-approval-tile__decision">{{ a.postCheckApproval.decision }}</span>
|
||||
<span>{{ a.postCheckApproval.approverName }} · {{ a.postCheckApproval.date }}</span>
|
||||
<span>{{ a.postCheckApproval.remarks }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Application Journey — Date-wise Audit Trail</div>
|
||||
<div class="npa-timeline" style="padding: 4px 18px 18px">
|
||||
@for (h of a.stageHistory; track h.date + h.stage + h.outcome) {
|
||||
<div [class]="historyToneClass(h.outcome)">
|
||||
<span class="npa-timeline-item__icon">{{ stepMarker(a, h.stage) }}</span>
|
||||
<div class="npa-timeline-item__body">
|
||||
<div class="npa-timeline-item__row">
|
||||
<span class="npa-timeline-item__type">{{ stageLabels[h.stage] }} — {{ h.outcome }}</span>
|
||||
<span class="npa-timeline-item__date">{{ h.date }}</span>
|
||||
</div>
|
||||
<p class="npa-timeline-item__remarks">{{ h.remarks }}</p>
|
||||
<span class="npa-timeline-item__channel">{{ h.actionBy }}</span>
|
||||
</div>
|
||||
</div>
|
||||
} @empty {
|
||||
<p class="pg-empty">No activity recorded yet.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="pg-card">
|
||||
<div class="pos-section-label">Application Not Found</div>
|
||||
<div class="loan-action-card">
|
||||
<button type="button" class="pg-btn pg-btn--ghost" (click)="goBack()">‹ Back to Pipeline</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
Reference in New Issue
Block a user