fix(letter-mgmt): make share certificate bank identity dynamic and fix broken printing

- 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.
This commit is contained in:
2026-09-14 16:58:18 +05:30
parent ee5a430b17
commit b03efef84e
11 changed files with 279 additions and 41 deletions
@@ -21,8 +21,10 @@
</div>
<div class="certificate-header">
<div class="bank-name">Nexora Co-operative Bank Ltd.</div>
<div class="bank-sub">Mumbai, Maharashtra, India</div>
<div class="bank-name">{{ bankName }}</div>
@if (bankLocation) {
<div class="bank-sub">{{ bankLocation }}</div>
}
<div class="cert-title">SHARE CERTIFICATE</div>
<div class="cert-subtitle">Issued under the Common Seal of the Bank</div>
</div>
@@ -51,8 +53,8 @@
<strong class="highlight-text">{{
sharesAccountData.currentMarketPrice | currency: sharesAccountData.currency.code : 'symbol-narrow'
}}</strong>
each, fully paid up, in <strong class="highlight-text">Nexora Co-operative Bank Ltd.</strong>, subject to
the memorandum, articles of association, and bye-laws of the bank.
each, fully paid up, in <strong class="highlight-text">{{ bankName }}</strong
>, subject to the memorandum, articles of association, and bye-laws of the bank.
</p>
<div class="issued-to-details">
@@ -75,7 +77,7 @@
<tbody>
<tr>
<td class="lbl-col">Branch</td>
<td class="val-col">{{ clientData?.officeName || 'Mumbai Main Branch' }}</td>
<td class="val-col">{{ clientData?.officeName || 'Head Office' }}</td>
<td class="lbl-col">Member Since</td>
<td class="val-col">
{{
@@ -140,6 +140,8 @@
}
.meta-value {
font-family: Arial, Helvetica, sans-serif;
font-variant-numeric: tabular-nums;
font-size: 15px;
font-weight: bold;
color: #322f2b;
@@ -226,12 +228,19 @@
.lbl-col {
background-color: #f7f5ee;
font-family: Georgia, 'Times New Roman', Times, serif;
font-weight: bold;
color: #7b6f63;
width: 25%;
}
// Every figure a member actually needs to read at a glance — share counts, amounts, dates, the
// resolution number — sits in this column. Georgia's numerals kept blurring into each other at
// this size, so values get a plain sans-serif with tabular figures instead; the label column
// keeps the certificate's own serif since it's static text, never a number to misread.
.val-col {
font-family: Arial, Helvetica, sans-serif;
font-variant-numeric: tabular-nums;
color: #322f2b;
width: 25%;
}
@@ -242,6 +251,7 @@
}
.lbl-col-highlight {
font-family: Georgia, 'Times New Roman', Times, serif;
font-weight: bold;
color: #0b3c5d;
font-size: 14px;
@@ -251,6 +261,8 @@
}
.val-col-highlight {
font-family: Arial, Helvetica, sans-serif;
font-variant-numeric: tabular-nums;
font-size: 16px;
font-weight: bold;
color: #b8860b; /* Dark Goldenrod */
@@ -270,6 +282,7 @@
padding: 8px 12px;
border: 1px solid #ede8db;
background-color: #f7f5ee;
font-family: Georgia, 'Times New Roman', Times, serif;
font-size: 11px;
font-weight: bold;
color: #7b6f63;
@@ -277,9 +290,14 @@
letter-spacing: 0.6px;
}
// Same reasoning as .val-col above: every cell here is a figure someone has to check against the
// register, so it reads in a plain sans-serif with tabular numerals rather than the certificate's
// decorative serif.
td {
padding: 9px 12px;
border: 1px solid #ede8db;
font-family: Arial, Helvetica, sans-serif;
font-variant-numeric: tabular-nums;
font-size: 13px;
color: #322f2b;
}
@@ -16,6 +16,7 @@ import {
MatDialogClose
} from '@angular/material/dialog';
import { ClientsService } from 'app/clients/clients.service';
import { OrganizationService } from 'app/organization/organization.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
@@ -44,6 +45,7 @@ export class ShareCertificateDialogComponent implements OnInit {
dialogRef = inject<MatDialogRef<ShareCertificateDialogComponent>>(MatDialogRef);
data = inject(MAT_DIALOG_DATA);
clientsService = inject(ClientsService);
private organizationService = inject(OrganizationService);
sharesAccountData: any;
clientId: string;
@@ -53,6 +55,11 @@ export class ShareCertificateDialogComponent implements OnInit {
panNumber: string = 'Not Available';
memberId: string = '';
/** The issuing bank's own name/location, as set on the Institution Profile — never a fixed name,
* since this certificate template is shared by every tenant this app runs for. */
bankName: string = 'Bank';
bankLocation: string = '';
certificateNo: string = '';
folioNo: string = '';
shareNumbersRange: string = '';
@@ -155,6 +162,24 @@ export class ShareCertificateDialogComponent implements OnInit {
},
error: () => {}
});
// Fetch the issuing bank's own name/location from the Institution Profile, set once under
// Admin > Organization for whichever bank this instance runs for — never a fixed name baked
// into the certificate itself.
this.organizationService.getInstitutionProfile().subscribe({
next: (profile: any) => {
this.bankName = profile?.statementHeaderName || profile?.bankName || this.bankName;
this.bankLocation = [
profile?.city,
profile?.state,
profile?.country
]
.filter((p) => !!p && String(p).trim())
.join(', ');
},
// A missing/incomplete profile must not stop the certificate from rendering.
error: () => {}
});
}
/**