share certificate and share product maker checker
This commit is contained in:
@@ -544,82 +544,315 @@ export function downloadLoanInterestPdf(record: LoanInterestRecord, fy: FyIntere
|
||||
|
||||
// ───────────────────── Share Certificate ─────────────────────
|
||||
export function buildShareCertificatePdf(r: ShareCertificateRecord): jsPDF {
|
||||
const c = newCtx(ACCENT_AMBER);
|
||||
const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
|
||||
const pageW = doc.internal.pageSize.getWidth();
|
||||
const pageH = doc.internal.pageSize.getHeight();
|
||||
const margin = 20;
|
||||
const contentW = pageW - 40;
|
||||
|
||||
const gold: [number, number, number] = [
|
||||
197,
|
||||
160,
|
||||
89
|
||||
]; // #c5a059
|
||||
const darkBlue: [number, number, number] = [
|
||||
11,
|
||||
60,
|
||||
93
|
||||
]; // #0b3c5d
|
||||
const textDark: [number, number, number] = [
|
||||
44,
|
||||
37,
|
||||
32
|
||||
]; // #2c2520
|
||||
const textMuted: [number, number, number] = [
|
||||
123,
|
||||
111,
|
||||
99
|
||||
]; // #7b6f63
|
||||
const lightBg: [number, number, number] = [
|
||||
253,
|
||||
252,
|
||||
247
|
||||
]; // #fdfcf7
|
||||
const containerBg: [number, number, number] = [
|
||||
250,
|
||||
248,
|
||||
245
|
||||
]; // #faf8f5
|
||||
|
||||
// 1. Draw Cream Background (page body)
|
||||
doc.setFillColor(containerBg[0], containerBg[1], containerBg[2]);
|
||||
doc.rect(0, 0, pageW, pageH, 'F');
|
||||
|
||||
// 2. Draw Textured cream background inside the outer border
|
||||
doc.setFillColor(lightBg[0], lightBg[1], lightBg[2]);
|
||||
doc.rect(12, 12, pageW - 24, pageH - 24, 'F');
|
||||
|
||||
// 3. Draw Gold Double Borders (Outer and Inner)
|
||||
doc.setDrawColor(gold[0], gold[1], gold[2]);
|
||||
|
||||
// Outer double line
|
||||
doc.setLineWidth(1.2);
|
||||
doc.rect(12, 12, pageW - 24, pageH - 24, 'D');
|
||||
|
||||
doc.setLineWidth(0.3);
|
||||
doc.rect(13.8, 13.8, pageW - 27.6, pageH - 27.6, 'D');
|
||||
|
||||
// Thin interior border line
|
||||
doc.setDrawColor(220, 209, 189); // light brown/grey
|
||||
doc.setLineWidth(0.2);
|
||||
doc.rect(17, 17, pageW - 34, pageH - 34, 'D');
|
||||
|
||||
// 4. Certificate Header
|
||||
let y = 28;
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(22);
|
||||
doc.setTextColor(darkBlue[0], darkBlue[1], darkBlue[2]);
|
||||
doc.text('NEXORA CO-OPERATIVE BANK LTD.', pageW / 2, y, { align: 'center' });
|
||||
|
||||
y += 5;
|
||||
doc.setFont('times', 'italic');
|
||||
doc.setFontSize(10.5);
|
||||
doc.setTextColor(textMuted[0], textMuted[1], textMuted[2]);
|
||||
doc.text('Mumbai, Maharashtra, India', pageW / 2, y, { align: 'center' });
|
||||
|
||||
y += 4;
|
||||
doc.setDrawColor(242, 237, 213); // light gold separator
|
||||
doc.setLineWidth(1.5);
|
||||
doc.line(22, y, pageW - 22, y);
|
||||
|
||||
y += 9;
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(24);
|
||||
doc.setTextColor(gold[0], gold[1], gold[2]);
|
||||
doc.text('SHARE CERTIFICATE', pageW / 2, y, { align: 'center' });
|
||||
|
||||
y += 5.5;
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(7.5);
|
||||
doc.setTextColor(textMuted[0], textMuted[1], textMuted[2]);
|
||||
doc.text('ISSUED UNDER THE COMMON SEAL OF THE BANK', pageW / 2, y, { align: 'center' });
|
||||
|
||||
y += 4;
|
||||
doc.setDrawColor(242, 237, 213); // separator
|
||||
doc.setLineWidth(0.3);
|
||||
doc.line(22, y, pageW - 22, y);
|
||||
|
||||
// 5. Meta Row (Certificate No, Folio No, Date of Issue)
|
||||
y += 7;
|
||||
|
||||
// Column 1: Certificate No
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(7.5);
|
||||
doc.setTextColor(textMuted[0], textMuted[1], textMuted[2]);
|
||||
doc.text('CERTIFICATE NO.', 25, y);
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(textDark[0], textDark[1], textDark[2]);
|
||||
doc.text(r.certificateNo, 25, y + 4.5);
|
||||
|
||||
// Column 2: Folio No (Centered)
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(7.5);
|
||||
doc.setTextColor(textMuted[0], textMuted[1], textMuted[2]);
|
||||
doc.text('FOLIO NO.', pageW / 2, y, { align: 'center' });
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(textDark[0], textDark[1], textDark[2]);
|
||||
doc.text(r.folioNo, pageW / 2, y + 4.5, { align: 'center' });
|
||||
|
||||
// Column 3: Date of Issue (Right Aligned)
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(7.5);
|
||||
doc.setTextColor(textMuted[0], textMuted[1], textMuted[2]);
|
||||
doc.text('DATE OF ISSUE', pageW - 25, y, { align: 'right' });
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(textDark[0], textDark[1], textDark[2]);
|
||||
doc.text(formatLongDate(r.issueDate), pageW - 25, y + 4.5, { align: 'right' });
|
||||
|
||||
y += 8;
|
||||
doc.setDrawColor(242, 237, 213);
|
||||
doc.setLineWidth(0.3);
|
||||
doc.line(22, y, pageW - 22, y);
|
||||
|
||||
// 6. Centered Body Section
|
||||
y += 9;
|
||||
const classLabel = r.shareClass === 'PREFERENCE' ? 'Preference' : 'Ordinary';
|
||||
|
||||
let y = paintLetterhead(c, 'Share Certificate', 'CERTIFICATE REFERENCE', r.certificateNo, 'Folio No.', r.folioNo);
|
||||
doc.setFont('times', 'normal');
|
||||
doc.setFontSize(11);
|
||||
doc.setTextColor(textDark[0], textDark[1], textDark[2]);
|
||||
|
||||
y = paintAddressee(c, y, 'Issued To,', r.memberName, r.memberId, r.memberPan, r.memberAddress);
|
||||
|
||||
// Centered, dramatic body paragraph (mirrors the highlighted "centered" style in the preview)
|
||||
if (y > c.pageH - c.margin - 80) {
|
||||
c.doc.addPage();
|
||||
setFill(c, c.accent);
|
||||
c.doc.rect(0, 0, c.pageW, 4, 'F');
|
||||
y = c.margin;
|
||||
const bodyText = `This is to certify that ${r.memberName} is the registered holder of ${r.shareCount.toLocaleString('en-IN')} (${classLabel}) shares of Rs. ${r.faceValue.toLocaleString('en-IN')} each, fully paid up, in Nexora Co-operative Bank Ltd., subject to the memorandum, articles of association, and bye-laws of the bank.`;
|
||||
const bodyLines = doc.splitTextToSize(bodyText, pageW - 50);
|
||||
for (const line of bodyLines) {
|
||||
doc.text(line, pageW / 2, y, { align: 'center' });
|
||||
y += 5.5;
|
||||
}
|
||||
const ctaText = `This is to certify that ${r.memberName} is the registered holder of ${r.shareCount.toLocaleString('en-IN')} (${classLabel}) shares of Rs. ${r.faceValue.toLocaleString('en-IN')} each, fully paid up, in ${BANK_NAME}, subject to the bye-laws of the bank.`;
|
||||
const ctaLines = c.doc.splitTextToSize(ctaText, c.contentW - 10);
|
||||
const ctaH = ctaLines.length * 5 + 6;
|
||||
setFill(c, c.accentTint);
|
||||
c.doc.rect(c.margin, y, c.contentW, ctaH, 'F');
|
||||
setFill(c, c.accent);
|
||||
c.doc.rect(c.margin, y, 1.8, ctaH, 'F');
|
||||
c.doc.setFont('times', 'bold');
|
||||
c.doc.setFontSize(11);
|
||||
setText(c, C_TEXT);
|
||||
c.doc.text(ctaLines, c.margin + 6, y + 5, { align: 'justify', maxWidth: c.contentW - 10 });
|
||||
y += ctaH + 6;
|
||||
|
||||
y = paintSummary(
|
||||
c,
|
||||
y,
|
||||
[
|
||||
[
|
||||
'CERTIFICATE NO.',
|
||||
r.certificateNo,
|
||||
'FOLIO NO.',
|
||||
r.folioNo
|
||||
],
|
||||
[
|
||||
'BRANCH',
|
||||
r.branch,
|
||||
'MEMBER SINCE',
|
||||
formatLongDate(r.joiningDate)
|
||||
],
|
||||
[
|
||||
'SHARE CLASS',
|
||||
classLabel,
|
||||
'DATE OF ISSUE',
|
||||
formatLongDate(r.issueDate)
|
||||
],
|
||||
[
|
||||
'SHARE NUMBERS',
|
||||
`${r.shareNoFrom.toLocaleString('en-IN')} – ${r.shareNoTo.toLocaleString('en-IN')}`,
|
||||
'DIVIDEND PAID',
|
||||
`Rs. ${r.dividendYtd.toLocaleString('en-IN')}`
|
||||
]
|
||||
],
|
||||
{
|
||||
label: 'TOTAL FACE VALUE',
|
||||
value: `Rs. ${(r.shareCount * r.faceValue).toLocaleString('en-IN')}`
|
||||
// 7. ISSUED TO Box
|
||||
y += 3;
|
||||
const addrLines = doc.splitTextToSize(r.memberAddress || '', pageW - 60);
|
||||
const boxH = 26 + addrLines.length * 4;
|
||||
|
||||
doc.setFillColor(253, 252, 247);
|
||||
doc.setDrawColor(230, 225, 210);
|
||||
doc.rect(25, y, pageW - 50, boxH, 'FD');
|
||||
|
||||
let boxY = y + 6;
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(8);
|
||||
doc.setTextColor(gold[0], gold[1], gold[2]);
|
||||
doc.text('ISSUED TO:', 30, boxY);
|
||||
|
||||
boxY += 5.5;
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(13);
|
||||
doc.setTextColor(darkBlue[0], darkBlue[1], darkBlue[2]);
|
||||
doc.text(r.memberName, 30, boxY);
|
||||
|
||||
boxY += 5.5;
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(9);
|
||||
doc.setTextColor(textDark[0], textDark[1], textDark[2]);
|
||||
let idLine = `Member ID: ${r.memberId}`;
|
||||
if (r.memberPan && r.memberPan !== 'Not Available' && r.memberPan !== '—') {
|
||||
idLine += ` · PAN: ${r.memberPan}`;
|
||||
}
|
||||
doc.text(idLine, 30, boxY);
|
||||
|
||||
boxY += 5;
|
||||
doc.setFontSize(8.5);
|
||||
doc.setTextColor(textMuted[0], textMuted[1], textMuted[2]);
|
||||
doc.text(addrLines, 30, boxY);
|
||||
|
||||
y += boxH + 8;
|
||||
|
||||
// 8. Details Grid
|
||||
const tableW = pageW - 50;
|
||||
const tableColW = tableW / 4;
|
||||
const tableRowH = 8.5;
|
||||
|
||||
const drawTableCell = (
|
||||
x: number,
|
||||
tblY: number,
|
||||
w: number,
|
||||
h: number,
|
||||
text: string,
|
||||
isLabel: boolean,
|
||||
align: 'left' | 'right' = 'left'
|
||||
) => {
|
||||
if (isLabel) {
|
||||
doc.setFillColor(253, 252, 247);
|
||||
doc.rect(x, tblY, w, h, 'F');
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(8);
|
||||
doc.setTextColor(gold[0], gold[1], gold[2]);
|
||||
} else {
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(9.5);
|
||||
doc.setTextColor(darkBlue[0], darkBlue[1], darkBlue[2]);
|
||||
}
|
||||
);
|
||||
doc.setDrawColor(220, 209, 189);
|
||||
doc.setLineWidth(0.25);
|
||||
doc.rect(x, tblY, w, h, 'D');
|
||||
doc.text(text, align === 'right' ? x + w - 3 : x + 3, tblY + h / 2 + 1.2, { align });
|
||||
};
|
||||
|
||||
y = paintPara(
|
||||
c,
|
||||
// Row 1
|
||||
drawTableCell(25, y, tableColW, tableRowH, 'Branch', true);
|
||||
drawTableCell(25 + tableColW, y, tableColW, tableRowH, r.branch || 'Mumbai Main Branch', false);
|
||||
drawTableCell(25 + tableColW * 2, y, tableColW, tableRowH, 'Member Since', true);
|
||||
drawTableCell(25 + tableColW * 3, y, tableColW, tableRowH, formatLongDate(r.joiningDate), false);
|
||||
y += tableRowH;
|
||||
|
||||
// Row 2
|
||||
drawTableCell(25, y, tableColW, tableRowH, 'Share Class', true);
|
||||
drawTableCell(25 + tableColW, y, tableColW, tableRowH, classLabel, false);
|
||||
drawTableCell(25 + tableColW * 2, y, tableColW, tableRowH, 'Date of Issue', true);
|
||||
drawTableCell(25 + tableColW * 3, y, tableColW, tableRowH, formatLongDate(r.issueDate), false);
|
||||
y += tableRowH;
|
||||
|
||||
// Row 3
|
||||
drawTableCell(25, y, tableColW, tableRowH, 'Share Numbers', true);
|
||||
drawTableCell(
|
||||
25 + tableColW,
|
||||
y,
|
||||
`Given under the common seal of the bank this ${new Date().toLocaleDateString('en-IN', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})}.`
|
||||
tableColW,
|
||||
tableRowH,
|
||||
`${r.shareNoFrom.toLocaleString('en-IN')} – ${r.shareNoTo.toLocaleString('en-IN')}`,
|
||||
false
|
||||
);
|
||||
drawTableCell(25 + tableColW * 2, y, tableColW, tableRowH, 'Dividend Paid', true);
|
||||
drawTableCell(25 + tableColW * 3, y, tableColW, tableRowH, `Rs. ${r.dividendYtd.toLocaleString('en-IN')}`, false);
|
||||
y += tableRowH;
|
||||
|
||||
y = paintSignoff(c, y + 6, r.branch);
|
||||
paintFooter(c, y + 4);
|
||||
return c.doc;
|
||||
// Row 4 (Highlight)
|
||||
doc.setFillColor(darkBlue[0], darkBlue[1], darkBlue[2]);
|
||||
doc.rect(25, y, tableColW * 2, tableRowH, 'F');
|
||||
doc.rect(25 + tableColW * 2, y, tableColW * 2, tableRowH, 'F');
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(8.5);
|
||||
doc.setTextColor(255, 255, 255);
|
||||
doc.text('TOTAL FACE VALUE', 28, y + tableRowH / 2 + 1.2);
|
||||
|
||||
doc.setFont('times', 'bold');
|
||||
doc.setFontSize(11);
|
||||
doc.text(`Rs. ${(r.shareCount * r.faceValue).toLocaleString('en-IN')}`, pageW - 28, y + tableRowH / 2 + 1.2, {
|
||||
align: 'right'
|
||||
});
|
||||
y += tableRowH + 8;
|
||||
|
||||
// 9. Footer Text (Given under common seal...)
|
||||
doc.setFont('times', 'italic');
|
||||
doc.setFontSize(10.5);
|
||||
doc.setTextColor(textDark[0], textDark[1], textDark[2]);
|
||||
|
||||
const sealDay = new Date().getDate();
|
||||
const sealMonthYear = new Date().toLocaleDateString('en-IN', { month: 'long', year: 'numeric' });
|
||||
const sealPhrase = `Given under the common seal of the bank this ${sealDay} day of ${sealMonthYear}.`;
|
||||
doc.text(sealPhrase, pageW / 2, y, { align: 'center' });
|
||||
|
||||
y += 8;
|
||||
|
||||
// 10. Seal Circle (left) and Signatory block (right)
|
||||
const sealX = 50;
|
||||
const sealY = y + 14;
|
||||
doc.setDrawColor(gold[0], gold[1], gold[2]); // Gold
|
||||
doc.setLineWidth(0.6);
|
||||
doc.circle(sealX, sealY, 14, 'D');
|
||||
|
||||
doc.setLineWidth(0.3);
|
||||
doc.setLineDashPattern(
|
||||
[
|
||||
1,
|
||||
1
|
||||
],
|
||||
0
|
||||
);
|
||||
doc.circle(sealX, sealY, 11, 'D');
|
||||
doc.setLineDashPattern([], 0);
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(7.5);
|
||||
doc.setTextColor(gold[0], gold[1], gold[2]);
|
||||
doc.text('COMMON', sealX, sealY - 1, { align: 'center' });
|
||||
doc.text('SEAL', sealX, sealY + 3.5, { align: 'center' });
|
||||
|
||||
// Signatory Line
|
||||
doc.setDrawColor(textMuted[0], textMuted[1], textMuted[2]);
|
||||
doc.setLineWidth(0.3);
|
||||
doc.line(pageW - 75, y + 20, pageW - 25, y + 20);
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(9.5);
|
||||
doc.setTextColor(textDark[0], textDark[1], textDark[2]);
|
||||
doc.text('Authorised Signatory', pageW - 50, y + 25, { align: 'center' });
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
export function downloadShareCertificatePdf(r: ShareCertificateRecord, dsc?: BankUserDsc): void {
|
||||
|
||||
Reference in New Issue
Block a user