155 lines
4.0 KiB
TypeScript
155 lines
4.0 KiB
TypeScript
/**
|
||
* 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/.
|
||
*/
|
||
|
||
import { Injectable } from '@angular/core';
|
||
import { Observable, delay, of, throwError } from 'rxjs';
|
||
import { demoBorrower, demoBorrowers, demoSummary } from './loan-eligibility.mock';
|
||
|
||
export type BorrowerCategory = 'GOOD' | 'WATCH' | 'BAD';
|
||
|
||
export interface LoanStageRow {
|
||
stage: string;
|
||
label: string;
|
||
dpdRange: string;
|
||
group: 'PERFORMING' | 'NPA' | 'CLOSED';
|
||
loans: number;
|
||
outstanding: number;
|
||
overdue: number;
|
||
}
|
||
|
||
export interface LoanProductRow {
|
||
productId: number;
|
||
productName: string;
|
||
activeLoans: number;
|
||
performingLoans: number;
|
||
npaLoans: number;
|
||
outstanding: number;
|
||
npaOutstanding: number;
|
||
}
|
||
|
||
/** Loan book health as on the business date (RBI asset classes, products, borrowers). */
|
||
export interface LoanEligibilitySummary {
|
||
asOnDate: string;
|
||
activeLoans: number;
|
||
activeOutstanding: number;
|
||
performingLoans: number;
|
||
performingOutstanding: number;
|
||
npaLoans: number;
|
||
npaOutstanding: number;
|
||
grossNpaPct: number;
|
||
closedLoans: number;
|
||
writtenOffLoans: number;
|
||
writtenOffAmount: number;
|
||
/** Share linking to borrowing across members with running loans. */
|
||
shareRequired: number;
|
||
shareHeld: number;
|
||
shareShortMembers: number;
|
||
shareShortfall: number;
|
||
goodBorrowers: number;
|
||
watchBorrowers: number;
|
||
badBorrowers: number;
|
||
stages: LoanStageRow[];
|
||
products: LoanProductRow[];
|
||
notes: string[];
|
||
}
|
||
|
||
export interface BorrowerLoan {
|
||
loanId: number;
|
||
accountNo: string;
|
||
productName: string;
|
||
status: string;
|
||
stage: string;
|
||
dpd: number;
|
||
emisPaid: number;
|
||
emisLate: number;
|
||
disbursed: number;
|
||
outstanding: number;
|
||
overdue: number;
|
||
sharePct: number;
|
||
shareRequired: number;
|
||
}
|
||
|
||
export interface AccountMonth {
|
||
month: string;
|
||
credits: number;
|
||
debits: number;
|
||
avgBalance: number;
|
||
returns: number;
|
||
}
|
||
|
||
/** Savings / current account statement, month by month. */
|
||
export interface BorrowerAccount {
|
||
type: 'Savings' | 'Current';
|
||
accountNo: string;
|
||
months: AccountMonth[];
|
||
}
|
||
|
||
export interface ProductEligibility {
|
||
productId: number;
|
||
productName: string;
|
||
eligible: boolean;
|
||
eligibleAmount?: number;
|
||
sharePct: number;
|
||
/** Shares to be taken with this loan (incl. any existing shortfall). */
|
||
shareToBuy?: number;
|
||
netInHand?: number;
|
||
minAmount?: number;
|
||
maxAmount?: number;
|
||
passed: string[];
|
||
failed: string[];
|
||
notes: string[];
|
||
}
|
||
|
||
export interface Borrower {
|
||
clientId: number;
|
||
clientName: string;
|
||
clientAccountNo: string;
|
||
category: BorrowerCategory;
|
||
activeLoans: number;
|
||
closedLoans: number;
|
||
writtenOffLoans: number;
|
||
outstanding: number;
|
||
maxDpd: number;
|
||
worstStage?: string;
|
||
monthlyIncome?: number;
|
||
age?: number;
|
||
depositBalance: number;
|
||
currentEmi: number;
|
||
eligibleProducts: number;
|
||
/** Repayment record over all loans: EMIs fallen due and how many were paid late. */
|
||
emisPaid: number;
|
||
emisLate: number;
|
||
shareHeld: number;
|
||
shareRequired: number;
|
||
/** Six-month averages of the operative account. */
|
||
accountType: 'Savings' | 'Current';
|
||
avgCredits: number;
|
||
avgDebits: number;
|
||
returns: number;
|
||
accounts?: BorrowerAccount[];
|
||
loans?: BorrowerLoan[];
|
||
eligibility?: ProductEligibility[];
|
||
}
|
||
|
||
/** Loan Eligibility (AI / Intelligence – Lending & Risk): demo data, worked out on the eligibility rules. */
|
||
@Injectable({ providedIn: 'root' })
|
||
export class LoanEligibilityService {
|
||
getSummary(): Observable<LoanEligibilitySummary> {
|
||
return of(demoSummary()).pipe(delay(2800));
|
||
}
|
||
|
||
getBorrowers(category: BorrowerCategory | '', search: string): Observable<Borrower[]> {
|
||
return of(demoBorrowers(category, search)).pipe(delay(700));
|
||
}
|
||
|
||
getBorrower(clientId: number): Observable<Borrower> {
|
||
const b = demoBorrower(clientId);
|
||
return b ? of(b).pipe(delay(900)) : throwError(() => new Error('Borrower not found'));
|
||
}
|
||
}
|