/** * 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 { return of(demoSummary()).pipe(delay(2800)); } getBorrowers(category: BorrowerCategory | '', search: string): Observable { return of(demoBorrowers(category, search)).pipe(delay(700)); } getBorrower(clientId: number): Observable { const b = demoBorrower(clientId); return b ? of(b).pipe(delay(900)) : throwError(() => new Error('Borrower not found')); } }