- Certificate PDFs, the preview dialog, and both share-certificate list views (demo + live) now pull bank name/address/reg no/seal text from the Institution Profile instead of a hardcoded "Nexora" identity. - Certificate preview dialog print CSS was clipped to the dialog's on-screen scroll viewport (maxHeight config); switched to position:fixed + unclipped overlay ancestors so the full certificate prints, not just a border sliver. - Global print stylesheet now also hides the app shell's real 2-row nav menu (.main-header/.kpi-ticker, not routed through <mifosx-toolbar>) and frees the CDK sidenav/tab-body scroll clipping, so printing any page no longer captures the nav chrome or gets cut off to one screen's height.
363 lines
11 KiB
TypeScript
363 lines
11 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 { Component, OnInit, 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 { OrganizationService } from 'app/organization/organization.service';
|
|
|
|
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,
|
|
setBankIdentity
|
|
} 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 implements OnInit {
|
|
private dialogRef = inject(MatDialogRef<CertificatePreviewDialogComponent>);
|
|
private organizationService = inject(OrganizationService);
|
|
data: CertificatePreviewDialogData = inject(MAT_DIALOG_DATA);
|
|
|
|
/** The issuing bank's own details, fetched from the Institution Profile — this dialog is shared
|
|
* by every certificate type and every tenant, so nothing here is a fixed name. */
|
|
bankName = 'Bank';
|
|
bankAddress = '';
|
|
bankRegNo = '';
|
|
bankContact = '';
|
|
bankSealPrimary = 'BANK';
|
|
bankSealSecondary = '';
|
|
|
|
ngOnInit(): void {
|
|
this.organizationService.getInstitutionProfile().subscribe({
|
|
next: (profile: any) => {
|
|
this.bankName = profile?.statementHeaderName || profile?.bankName || this.bankName;
|
|
this.bankAddress = [
|
|
profile?.addressLine1,
|
|
profile?.addressLine2,
|
|
profile?.city
|
|
]
|
|
.filter((p: any) => !!p && String(p).trim())
|
|
.join(', ');
|
|
this.bankRegNo = profile?.licenseNumber ? `RBI Lic. No. ${profile.licenseNumber}` : '';
|
|
this.bankContact = [
|
|
profile?.contactEmail,
|
|
profile?.contactPhone
|
|
]
|
|
.filter((p: any) => !!p && String(p).trim())
|
|
.join(' · ');
|
|
this.bankSealPrimary = (profile?.shortName || this.bankName).toUpperCase();
|
|
this.bankSealSecondary = profile?.bankType
|
|
? profile.bankType.replace(/_/g, ' ').toUpperCase() + (/BANK$/i.test(profile.bankType) ? '' : ' BANK')
|
|
: '';
|
|
|
|
// Every certificate PDF this dialog can build (Download button below, or the list page's
|
|
// own download icon opened without previewing first) reads the same shared identity, so
|
|
// set it here once the real profile is in — never leave a PDF carrying the placeholder.
|
|
setBankIdentity(profile || {});
|
|
},
|
|
// A missing/incomplete profile must not stop the certificate from rendering.
|
|
error: () => {}
|
|
});
|
|
}
|
|
|
|
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 97 Declaration';
|
|
case 'form15gh':
|
|
return 'Form 121 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 '';
|
|
}
|
|
}
|
|
}
|