feat: letter-mgmt module + digital UPI/IMPS/ATM-POS reports + header redesign

letter-mgmt (new module):
- Notice page: overdue loan list with filters, KPI strip, bulk actions,
  call disposition logging (Promise-to-Pay, Not Reachable, etc.), Speed
  Post dispatch dialog with consignment + post-office tracking,
  PDF preview + download, call-contact dialog with primary / alt /
  family / nominee numbers and per-contact disposition history.
- Certificates: Loan Interest, Share, Balance, Form 60 (no-PAN flow),
  Form 15G/H (auto age-based), Hypothecation, NOC - each with shared
  preview dialog (six tone variants) and jsPDF generator using a
  common letterhead / summary-grid / signature-seal helper.

digital:
- IMPS module: report (P2A/P2P/Aadhaar tabs) + entry form with
  MMID/mobile lookup, NPCI switch resolve-to-A/C+IFSC BANL flow,
  sender autocomplete, hide sender field for P2P/Aadhaar, charges +
  18% GST + waive checkbox, per-sender today-only history dialog.
- UPI report with app-mix tab and full transaction filtering.
- ATM/POS/ECOM hub with three ticket cards routing to today/status/cycle.
- NEFT/RTGS entry: sender-account autocomplete, charges + GST + waive,
  field reorder (sender before amount), NEFT cap / RTGS floor
  validation, History button opening per-sender today's transactions.
- NEFT/RTGS report: horizontal-bar trend leaderboard replacing tall bars.

shell + theme:
- Header redesigned with IBM Plex Sans 12/500 typography, 11-column grid
  per row, top-accent hover indicator, bordered cells via grid-gap.
- Live KPI ticker below menu (CASH, TXN TODAY, CLEARING IN/OUT, NPA,
  CASA, CD RATIO, RBI CIRC, REPO) with marquee scroll, pause-on-hover,
  brand-blue gradient, amber bottom strip, pulsing red LIVE chip.
- Sidebar 'KYC Management' -> 'KYC & ReKYC Management'.
- Letter Mgmt / certificate menu items wired with green icon hints for
  items that have a working page.

access-control:
- MFA card layout compacted; method header inlines back button + title.

icons:
- Register faTruck/TruckFast, faEllipsisV/H, faCommentDots.
- Add IBM Plex Sans Google Fonts import to index.html.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-19 10:09:06 +05:30
co-authored by Claude Opus 4.7
parent 4322f37c6d
commit 325228c514
138 changed files with 27473 additions and 3997 deletions
@@ -0,0 +1,322 @@
/**
* 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 } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatTooltip } from '@angular/material/tooltip';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { FyInterestBreakup, LoanInterestRecord } from '../loan-interest/loan-interest.component';
import { ShareCertificateRecord } from '../share-certificate/share-certificate.component';
import { BalanceCertificateRecord } from '../balance-certificate/balance-certificate.component';
import { Form60Record } from '../form-60/form-60.component';
import { Form15Kind, Form15ghRecord } from '../form-15gh/form-15gh.component';
import { HypothecationRecord } from '../hypothecation/hypothecation.component';
import { NocRecord } from '../noc/noc.component';
import {
downloadBalanceCertificatePdf,
downloadForm15ghPdf,
downloadForm60Pdf,
downloadHypothecationPdf,
downloadLoanInterestPdf,
downloadNocPdf,
downloadShareCertificatePdf
} from '../certificate-pdf';
export interface InterestPreviewPayload {
record: LoanInterestRecord;
fy: FyInterestBreakup;
}
export interface BalancePreviewPayload {
record: BalanceCertificateRecord;
asOnDate: string;
}
export interface Form15ghPreviewPayload {
record: Form15ghRecord;
kind: Form15Kind;
age: number;
}
export type CertificateKind = 'interest' | 'share' | 'balance' | 'form60' | 'form15gh' | 'hypothecation' | 'noc';
export interface CertificatePreviewDialogData {
kind: CertificateKind;
loanInterest?: InterestPreviewPayload;
shareCertificate?: ShareCertificateRecord;
balance?: BalancePreviewPayload;
form60?: Form60Record;
form15gh?: Form15ghPreviewPayload;
hypothecation?: HypothecationRecord;
noc?: NocRecord;
}
@Component({
selector: 'mifosx-certificate-preview-dialog',
templateUrl: './certificate-preview-dialog.component.html',
styleUrls: ['./certificate-preview-dialog.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
CommonModule,
FaIconComponent,
MatTooltip
]
})
export class CertificatePreviewDialogComponent {
private dialogRef = inject(MatDialogRef<CertificatePreviewDialogComponent>);
data: CertificatePreviewDialogData = inject(MAT_DIALOG_DATA);
readonly bankName = 'Nexora Co-operative Bank Ltd.';
readonly bankAddress = 'Head Office, 4th Floor, Nexora Tower, Mumbai 400051';
readonly bankRegNo = 'RBI Lic. No. NX-2018/MH/482';
readonly bankContact = 'info@nexorabank.in · 022 6789 0000';
get isInterest(): boolean {
return this.data.kind === 'interest';
}
get isShare(): boolean {
return this.data.kind === 'share';
}
get isBalance(): boolean {
return this.data.kind === 'balance';
}
get isForm60(): boolean {
return this.data.kind === 'form60';
}
get isForm15gh(): boolean {
return this.data.kind === 'form15gh';
}
get isHypothecation(): boolean {
return this.data.kind === 'hypothecation';
}
get isNoc(): boolean {
return this.data.kind === 'noc';
}
get interest(): InterestPreviewPayload | undefined {
return this.data.loanInterest;
}
get share(): ShareCertificateRecord | undefined {
return this.data.shareCertificate;
}
get balance(): BalancePreviewPayload | undefined {
return this.data.balance;
}
get form60(): Form60Record | undefined {
return this.data.form60;
}
get form15gh(): Form15ghPreviewPayload | undefined {
return this.data.form15gh;
}
get hypothecation(): HypothecationRecord | undefined {
return this.data.hypothecation;
}
get noc(): NocRecord | undefined {
return this.data.noc;
}
/** Tone class on `.cpd-page` — drives the accent stripe + seal + pill colour. */
get pageToneClass(): string {
switch (this.data.kind) {
case 'share':
case 'form60':
return 'cpd-page--amber';
case 'form15gh':
return 'cpd-page--teal';
case 'hypothecation':
return 'cpd-page--red';
case 'noc':
return 'cpd-page--green';
case 'balance':
case 'interest':
default:
return '';
}
}
get title(): string {
switch (this.data.kind) {
case 'interest':
return 'Interest-Paid Certificate';
case 'share':
return 'Share Certificate';
case 'balance':
return 'Balance Certificate';
case 'form60':
return 'Form 60 Declaration';
case 'form15gh':
return this.form15gh?.kind === '15H' ? 'Form 15H Declaration' : 'Form 15G Declaration';
case 'hypothecation':
return 'Hypothecation Certificate';
case 'noc':
return 'No-Objection Certificate';
}
}
get refLine(): string {
if (this.isInterest && this.interest) {
return `${this.interest.record.accountNo} · ${this.interest.fy.fy}`;
}
if (this.isShare && this.share) {
return `${this.share.certificateNo} · Folio ${this.share.folioNo}`;
}
if (this.isBalance && this.balance) {
return `${this.balance.record.accountNo} · as on ${this.balance.asOnDate}`;
}
if (this.isForm60 && this.form60) {
return `${this.form60.customerId} · ${this.form60.transactionType}`;
}
if (this.isForm15gh && this.form15gh) {
return `${this.form15gh.record.customerId} · ${this.form15gh.kind} · ${this.form15gh.record.fy}`;
}
if (this.isHypothecation && this.hypothecation) {
return `${this.hypothecation.certificateNo} · ${this.hypothecation.accountNo}`;
}
if (this.isNoc && this.noc) {
return `${this.noc.nocNo} · ${this.noc.accountNo}`;
}
return '';
}
get issuedOn(): string {
return new Date().toISOString().slice(0, 10);
}
close(): void {
this.dialogRef.close();
}
printCertificate(): void {
window.print();
}
downloadPdf(): void {
if (this.isInterest && this.interest) {
downloadLoanInterestPdf(this.interest.record, this.interest.fy);
} else if (this.isShare && this.share) {
downloadShareCertificatePdf(this.share);
} else if (this.isBalance && this.balance) {
downloadBalanceCertificatePdf(this.balance.record, this.balance.asOnDate);
} else if (this.isForm60 && this.form60) {
downloadForm60Pdf(this.form60);
} else if (this.isForm15gh && this.form15gh) {
downloadForm15ghPdf(this.form15gh.record, this.form15gh.kind);
} else if (this.isHypothecation && this.hypothecation) {
downloadHypothecationPdf(this.hypothecation);
} else if (this.isNoc && this.noc) {
downloadNocPdf(this.noc);
}
}
nocLoanTypeLabel(): string {
const t = this.noc?.loanType;
switch (t) {
case 'AUTO':
return 'Auto Loan';
case 'HOUSING':
return 'Housing Loan';
case 'PERSONAL':
return 'Personal Loan';
case 'EDUCATION':
return 'Education Loan';
case 'GOLD':
return 'Gold Loan';
case 'BUSINESS':
return 'Business Loan';
case 'CC':
return 'Cash Credit';
case 'OD':
return 'Overdraft';
default:
return '';
}
}
nocAssetLabel(): string {
const t = this.noc?.assetType;
switch (t) {
case 'VEHICLE':
return 'Vehicle';
case 'PROPERTY':
return 'Property';
case 'GOLD':
return 'Gold / Bullion';
case 'EQUIPMENT':
return 'Equipment';
case 'STOCK_DEBTORS':
return 'Stock / Debtors';
case 'NONE':
return 'Unsecured';
default:
return '';
}
}
classLabelText(): string {
return this.share?.shareClass === 'PREFERENCE' ? 'Preference' : 'Ordinary';
}
accountTypeLabelText(): string {
const t = this.balance?.record.accountType;
switch (t) {
case 'SAVINGS':
return 'Savings';
case 'CURRENT':
return 'Current';
case 'FIXED_DEPOSIT':
return 'Fixed Deposit';
case 'RD':
return 'Recurring Deposit';
case 'NRE':
return 'NRE Savings';
case 'NRO':
return 'NRO Savings';
default:
return '';
}
}
idProofLabelText(): string {
const t = this.form60?.idProofType;
switch (t) {
case 'AADHAAR':
return 'Aadhaar Card';
case 'VOTER_ID':
return 'Voter Identity Card';
case 'DRIVING_LICENSE':
return 'Driving License';
case 'PASSPORT':
return 'Passport';
case 'RATION':
return 'Ration Card';
default:
return '';
}
}
assetLabelText(): string {
const t = this.hypothecation?.assetType;
switch (t) {
case 'VEHICLE':
return 'Vehicle';
case 'PLANT_MACHINERY':
return 'Plant & Machinery';
case 'STOCK_DEBTORS':
return 'Stock / Debtors';
case 'GOLD':
return 'Gold / Bullion';
case 'EQUIPMENT':
return 'Equipment';
default:
return '';
}
}
}