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:
2026-08-02 14:22:32 +05:30
parent b4608cbbe4
commit 5cd9998cfc
10 changed files with 4474 additions and 0 deletions
@@ -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 &amp; 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 &amp; Document Collection
</button>
</div>
</div>
</div>
}
<!-- ===================== KYC & DOCUMENTS ===================== -->
@if (a.currentStage === 'KYC_DOCUMENTS') {
<div class="pg-card">
<div class="pos-section-label">KYC &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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>