/** * 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 { Component, inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTooltip } from '@angular/material/tooltip'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { CertificatePreviewDialogComponent, CertificatePreviewDialogData } from '../certificate-preview-dialog/certificate-preview-dialog.component'; import { downloadLoanInterestPdf } from '../certificate-pdf'; export type LoanProductType = 'HOUSING' | 'PERSONAL' | 'AUTO' | 'EDUCATION' | 'GOLD' | 'BUSINESS'; export interface FyInterestBreakup { /** Financial year label e.g. "FY 2024-25". */ fy: string; openingBalance: number; closingBalance: number; principalPaid: number; interestPaid: number; } export interface LoanInterestRecord { accountNo: string; customerId: string; customerName: string; customerPan: string; customerAddress: string; branch: string; loanType: LoanProductType; sanctionedAmount: number; disbursementDate: string; /** Interest rate p.a. (e.g. 8.65) */ interestRate: number; fy: FyInterestBreakup[]; } @Component({ selector: 'mifosx-loan-interest-certificate', templateUrl: './loan-interest.component.html', styleUrls: ['../certificates.shared.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, CommonModule, FormsModule, FaIconComponent, MatTooltip ] }) export class LoanInterestCertificateComponent implements OnInit { private dialog = inject(MatDialog); private snack = inject(MatSnackBar); // ── Filters ── search = ''; fyFilter = ''; loanTypeFilter: '' | LoanProductType = ''; loans: LoanInterestRecord[] = []; /** All financial years present across the dataset (newest first). */ fyOptions: string[] = []; readonly loanTypeOptions: { value: LoanProductType; label: string }[] = [ { value: 'HOUSING', label: 'Housing Loan' }, { value: 'PERSONAL', label: 'Personal Loan' }, { value: 'AUTO', label: 'Auto Loan' }, { value: 'EDUCATION', label: 'Education Loan' }, { value: 'GOLD', label: 'Gold Loan' }, { value: 'BUSINESS', label: 'Business Loan' } ]; ngOnInit(): void { this.loans = seedLoanInterestRecords(); this.fyOptions = Array.from(new Set(this.loans.flatMap((l) => l.fy.map((f) => f.fy)))) .sort() .reverse(); // Default to most-recent FY if (this.fyOptions.length) { this.fyFilter = this.fyOptions[0]; } } // ── Derived list ── get filteredLoans(): LoanInterestRecord[] { const q = this.search.trim().toLowerCase(); return this.loans.filter((l) => { if (this.loanTypeFilter && l.loanType !== this.loanTypeFilter) return false; if (!q) return true; return ( l.accountNo.toLowerCase().includes(q) || l.customerName.toLowerCase().includes(q) || l.customerId.toLowerCase().includes(q) || l.customerPan.toLowerCase().includes(q) ); }); } loanTypeLabel(t: LoanProductType): string { return this.loanTypeOptions.find((o) => o.value === t)?.label || t; } selectedFyData(l: LoanInterestRecord): FyInterestBreakup | undefined { return l.fy.find((f) => f.fy === this.fyFilter) || l.fy[0]; } totalInterestForFy(): number { return this.filteredLoans.reduce((s, l) => s + (this.selectedFyData(l)?.interestPaid || 0), 0); } totalPrincipalForFy(): number { return this.filteredLoans.reduce((s, l) => s + (this.selectedFyData(l)?.principalPaid || 0), 0); } outstandingPrincipal(l: LoanInterestRecord): number { return this.selectedFyData(l)?.closingBalance ?? 0; } // ── Actions ── clearFilters(): void { this.search = ''; this.loanTypeFilter = ''; if (this.fyOptions.length) this.fyFilter = this.fyOptions[0]; } viewCertificate(l: LoanInterestRecord): void { const fy = this.selectedFyData(l); if (!fy) { this.snack.open('No interest data on file for the selected financial year.', 'OK', { duration: 2500 }); return; } const data: CertificatePreviewDialogData = { kind: 'interest', loanInterest: { record: l, fy } }; this.dialog.open(CertificatePreviewDialogComponent, { width: '880px', maxWidth: '96vw', maxHeight: '92vh', autoFocus: false, panelClass: 'cert-preview-dialog', data }); } downloadCertificate(l: LoanInterestRecord): void { const fy = this.selectedFyData(l); if (!fy) { this.snack.open('No interest data on file for the selected financial year.', 'OK', { duration: 2500 }); return; } downloadLoanInterestPdf(l, fy); this.snack.open(`Interest Certificate (${fy.fy}) downloaded.`, 'OK', { duration: 2400 }); } } // ───────────────────── Mock seed (top-level export) ───────────────────── export function seedLoanInterestRecords(): LoanInterestRecord[] { return [ { accountNo: 'LN10029384', customerId: 'CUST112233', customerName: 'Rohan S. Mehta', customerPan: 'ABCPM1234E', customerAddress: 'Flat 902, Skyline Apt, Andheri (E), Mumbai 400069', branch: 'Mumbai — Andheri (E)', loanType: 'HOUSING', sanctionedAmount: 4500000, disbursementDate: '2021-06-15', interestRate: 8.65, fy: [ { fy: 'FY 2024-25', openingBalance: 3620000, closingBalance: 3380000, principalPaid: 240000, interestPaid: 312500 }, { fy: 'FY 2023-24', openingBalance: 3840000, closingBalance: 3620000, principalPaid: 220000, interestPaid: 328200 }, { fy: 'FY 2022-23', openingBalance: 4050000, closingBalance: 3840000, principalPaid: 210000, interestPaid: 342800 } ] }, { accountNo: 'LN20034112', customerId: 'CUST334455', customerName: 'Anita Verma', customerPan: 'EFGHV5678A', customerAddress: 'B-12, Connaught Pl, New Delhi 110001', branch: 'New Delhi — Connaught Place', loanType: 'PERSONAL', sanctionedAmount: 600000, disbursementDate: '2022-09-10', interestRate: 12.5, fy: [ { fy: 'FY 2024-25', openingBalance: 380000, closingBalance: 220000, principalPaid: 160000, interestPaid: 41200 }, { fy: 'FY 2023-24', openingBalance: 520000, closingBalance: 380000, principalPaid: 140000, interestPaid: 56800 } ] }, { accountNo: 'LN50200077', customerId: 'CUST556677', customerName: 'Karthik Rao', customerPan: 'PQRSR2233Z', customerAddress: '24, MG Road, Bengaluru 560001', branch: 'Bengaluru — MG Road', loanType: 'AUTO', sanctionedAmount: 850000, disbursementDate: '2023-01-20', interestRate: 9.4, fy: [ { fy: 'FY 2024-25', openingBalance: 620000, closingBalance: 480000, principalPaid: 140000, interestPaid: 58800 }, { fy: 'FY 2023-24', openingBalance: 780000, closingBalance: 620000, principalPaid: 160000, interestPaid: 71200 } ] }, { accountNo: 'LN70010023', customerId: 'CUST889900', customerName: 'Vikram Joshi', customerPan: 'LMNOJ9988X', customerAddress: 'Office 3B, Koregaon Park Road, Pune 411001', branch: 'Pune — Koregaon Park', loanType: 'BUSINESS', sanctionedAmount: 5500000, disbursementDate: '2020-11-05', interestRate: 11.0, fy: [ { fy: 'FY 2024-25', openingBalance: 4200000, closingBalance: 3950000, principalPaid: 250000, interestPaid: 464800 }, { fy: 'FY 2023-24', openingBalance: 4480000, closingBalance: 4200000, principalPaid: 280000, interestPaid: 488600 }, { fy: 'FY 2022-23', openingBalance: 4760000, closingBalance: 4480000, principalPaid: 280000, interestPaid: 522300 } ] }, { accountNo: 'LN88990022', customerId: 'CUST990011', customerName: 'Arjun Banerjee', customerPan: 'TUVWB4455F', customerAddress: '42, Park Street, Kolkata 700016', branch: 'Kolkata — Park Street', loanType: 'EDUCATION', sanctionedAmount: 1200000, disbursementDate: '2022-04-22', interestRate: 9.85, fy: [ { fy: 'FY 2024-25', openingBalance: 920000, closingBalance: 790000, principalPaid: 130000, interestPaid: 93500 }, { fy: 'FY 2023-24', openingBalance: 1080000, closingBalance: 920000, principalPaid: 160000, interestPaid: 102200 } ] }, { accountNo: 'LN50200099', customerId: 'CUST667788', customerName: 'Lakshmi Naidu', customerPan: 'XYZAN7788Q', customerAddress: 'Plot 14, Road No. 3, Banjara Hills, Hyderabad 500034', branch: 'Hyderabad — Banjara Hills', loanType: 'GOLD', sanctionedAmount: 450000, disbursementDate: '2023-08-18', interestRate: 10.25, fy: [ { fy: 'FY 2024-25', openingBalance: 320000, closingBalance: 180000, principalPaid: 140000, interestPaid: 28400 } ] } ]; }