old ,new form16 working
// Paste this function into your it 2025-26 html.txt script (after calculateAndGenerate)
function downloadForm16PDF(taxRegime = null) {
// Safety check for jsPDF
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') {
alert("jsPDF library is missing. Please ensure it is loaded.");
return;
}
try {
// Determine selected regime: prefer parameter -> form select -> stored statement meta
const formRegime = (taxRegime || (document.getElementById('neol') ? document.getElementById('neol').value : null) || (window.latestStatement && window.latestStatement.meta && window.latestStatement.meta.slabType) || 'OLD IT SLAB').toString().toUpperCase();
const isNew = formRegime.startsWith('NEW');
// Get statement (must be generated first)
const S = window.latestStatement || {};
if (!S || !S.totals) {
// fallback: try to read minimal values from form
alert('Please run "Calculate and Generate Statement" first so Form16 can be generated.');
return;
}
// Shortcut helpers
const safeNum = v => {
if (v === null || v === undefined || v === "") return 0;
const n = Number(String(v).toString().replace(/,/g, ''));
return isNaN(n) ? 0 : Math.round(n);
};
const fmt = (n) => {
const v = safeNum(n);
return v === 0 ? "0" : v.toLocaleString('en-IN');
};
// Metadata mapping (prefer window.latestStatement.meta; fallback to form fields)
const meta = S.meta || {};
const _get = (id, fallback = '') => {
try {
const el = document.getElementById(id);
return el ? (el.value || fallback) : fallback;
} catch (e) { return fallback; }
};
const METADATA = {
nameEmp: (meta.ddoName || _get('ddoName') || '').toString().trim() || '',
nameDesig: (meta.name || meta.designation || _get('name') || _get('designation') || '').toString().trim() || '',
panDed: (meta.panDed || _get('ddoPan') || '').toString().trim() || 'PANNOTREQD',
tanDed: (meta.tanNo || _get('tanNo') || '').toString().trim() || '',
panEmp: (meta.panno || _get('panno') || '').toString().trim() || '',
fy: (meta.fy || '2025-26'),
ay: (meta.ay || '2026-27'),
periodFrom: (meta.periodFrom || '01/04/2025'),
periodTo: (meta.periodTo || '31/03/2026'),
footer: (meta.footer || "Designed By: K.SANYASI NAIDU, SGT, VISAKHAPATNAM"),
date: (meta.date || (new Date()).toLocaleDateString('en-GB')),
city: (meta.city || 'PADMANABHAM')
};
// Totals and inputs from window.latestStatement (these keys are created by your calculateAndGenerate)
const T = S.totals || {};
const I = S.inputs || {};
// Quarterly advance tax mapping: S.totals.totalAdvTax is used when monthly advTax not present
// But your calculateAndGenerate created monthly advTax overrides in window.latestStatement? It stored totalAdvTax in totals
// We will also check S.advTax or try to read adv-tax-override inputs when available.
const advMonthly = (S.advTax && Array.isArray(S.advTax) && S.advTax.length >= 12)
? S.advTax.map(v => safeNum(v))
: (() => {
// Attempt to read override inputs on page: advTax_Mar25 etc
const monthsKeys = ["Mar25","Apr25","May25","Jun25","Jul25","Aug25","Sep25","Oct25","Nov25","Dec25","Jan26","Feb26"];
const arr = [];
monthsKeys.forEach(k => {
const el = document.getElementById('advTax_' + k);
arr.push(el ? safeNum(el.value) : 0);
});
// if all zeros and we have defaultAdvanceTax, use default value
if (arr.every(a => a === 0)) {
const defAdv = safeNum(document.getElementById('defaultAdvanceTax') ? document.getElementById('defaultAdvanceTax').value : (T.totalAdvTax || 0));
if (defAdv > 0) return new Array(12).fill(defAdv);
}
return arr;
})();
// Compute quarter amounts from monthly advMonthly
const qFromMonthly = (() => {
if (!advMonthly || advMonthly.length < 12) {
// fallback: use totals.totalAdvTax split evenly / or map to zero
const totalAdv = safeNum(T.totalAdvTax || T.totalAdvTax || 0);
const per = Math.floor(totalAdv / 4);
return [per, per, per, totalAdv - per*3];
}
const q1 = advMonthly.slice(0,3).reduce((a,b)=>a+b,0);
const q2 = advMonthly.slice(3,6).reduce((a,b)=>a+b,0);
const q3 = advMonthly.slice(6,9).reduce((a,b)=>a+b,0);
const q4 = advMonthly.slice(9,12).reduce((a,b)=>a+b,0);
return [q1,q2,q3,q4];
})();
// Map data for NEW or OLD
// Base gross - use totalGross from totals
const grossTotal = safeNum(T.totalGross || T.totalPay || 0);
// If NEW regime: only gross and standard deduction 75k (per your requirement)
let DATA = {};
if (isNew) {
DATA = {
regimeLabel: "NEW IT SLAB",
gross: grossTotal,
standardDeduction: 75000,
hraExemption: 0,
pt: 0,
S80C_TotalGross: 0,
S80C_Deductible: 0,
S13_EWF_Gross: 0,
S13_80D_Gross: 0,
S13_EWF_Deductible: 0,
S13_80D_Deductible: 0,
housingLoanInterest: 0,
educationLoan: 0,
rebate: safeNum(T.rebate || 0),
totalTDS: safeNum(T.totalAdvTax || T.totalTDS || 0),
totalPaid: grossTotal
};
} else {
// OLD: take values from T and I
// HRA exemption: T.hraExemption computed in calculateAndGenerate
const hraExemption = safeNum(T.hraExemption || 0);
// PT: T.totalPtax
const pt = safeNum(T.totalPtax || 0);
// 80C: sec80C_Allowed stored in totals earlier (sec80C_Allowed)
const s80cAllowed = safeNum(T.sec80C_Allowed || T.sec80C || 0);
const s80cGross = safeNum(T.sec80C_Total || T.sec80C_Total || 0);
const housingLoanInterest = safeNum(T.housingLoanInterest || I.housingLoanInterest || 0);
const educationLoan = safeNum(T.educationLoan || I.educationLoan || 0);
// S13 totals (EWF, 80D) were stored as totals.totalEwf / totalEhs etc - map conservatively
const S13_EWF_Gross = 0; // your calculateAndGenerate used EWF monthly; we keep gross 0 for these unless you stored them explicitly
const S13_80D_Gross = 0;
DATA = {
regimeLabel: "OLD IT SLAB",
gross: grossTotal,
standardDeduction: safeNum(T.stdDeduction || T.stdDeduction || 50000),
hraExemption: hraExemption,
pt: pt,
S80C_TotalGross: s80cGross,
S80C_Deductible: s80cAllowed,
S13_EWF_Gross: S13_EWF_Gross,
S13_80D_Gross: S13_80D_Gross,
S13_EWF_Deductible: 0,
S13_80D_Deductible: 0,
housingLoanInterest: housingLoanInterest,
educationLoan: educationLoan,
rebate: safeNum(T.rebate || 0),
totalTDS: safeNum(T.totalAdvTax || T.totalTDS || 0),
totalPaid: grossTotal
};
}
// Derived standard calculations (these follow your existing logic naming)
const balance = DATA.gross - (DATA.hraExemption || 0);
const totalDeductionsU16 = (DATA.standardDeduction || 0) + (DATA.pt || 0);
const incSal = balance - totalDeductionsU16;
const gti = incSal; // assuming other incomes are zero
const S11_TotalSavings = DATA.S80C_Deductible || 0;
const taxableIncome_S12 = Math.max(0, gti - S11_TotalSavings);
const S13_OtherTotalGross = (DATA.S13_EWF_Gross || 0) + (DATA.S13_80D_Gross || 0);
const aggregateDeductibleS13 = (DATA.S13_EWF_Deductible || 0) + (DATA.S13_80D_Deductible || 0);
const S14_TotalDeductions = S11_TotalSavings + aggregateDeductibleS13;
const totalIncome = Math.max(0, gti - S14_TotalDeductions);
// Tax values: prefer stored totals (your calculateAndGenerate stored oldTax/newTax)
const taxOnIncome = isNew ? safeNum(T.newTax || T.newTax || 0) : safeNum(T.oldTax || T.oldTax || 0);
const rebate = safeNum(T.rebate || 0);
const taxAfterRebate = Math.max(0, taxOnIncome - rebate);
const cess = Math.round(taxAfterRebate * 0.04);
const taxPayableRounded = Math.round(taxAfterRebate + cess);
const taxPayableFinal = safeNum(T.totalTax || taxPayableRounded || 0);
const taxPayableResult = taxPayableFinal - (DATA.totalTDS || 0);
const verifiedAmount = taxPayableFinal;
const annexureA_Total = taxPayableFinal;
// --- Now generate PDF using jsPDF (keeps similar layout to your existing function) ---
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'pt', 'a4');
// Page geometry constants matching your file
const PG_W = 595.28;
const PG_H = 841.89;
const LM = 25; // Left Margin
const RM = 570; // Right Margin
const CW = RM - LM; // Content Width
let y = 30; // Vertical Cursor
const ROW_H_M = 18; // Minimum Row Height
const SECTION_SPACING = 6;
// Small helpers (mirrors your existing functions)
const font = (style, size) => { doc.setFont("helvetica", style); doc.setFontSize(size); };
const txt = (str, x, yy, options = {}) => { if (str === null || str === undefined) return; doc.text(String(str), x, yy, options); };
const right = (str, x, yy) => { if (str === null || str === undefined) return; doc.text(String(str), x, yy, { align: "right" }); };
const center = (str, yy) => { if (str === null || str === undefined) return; doc.text(String(str), PG_W / 2, yy, { align: "center" }); };
// Header & Part A similar to your existing PDF layout
doc.setLineWidth(0.6);
doc.rect(LM - 5, 20, CW + 10, 30); font("bold", 12); center("FORM NO.16", y + 12); y += 20;
doc.rect(LM - 5, y, CW + 10, 16); font("normal", 10); center(`(${DATA.regimeLabel}) [See rule 31(1)(a)]`, y + 10); y += 16;
doc.rect(LM - 5, y, CW + 10, 16); font("bold", 12); center("PART-A", y + 10); y += 16;
doc.rect(LM - 5, y, CW + 10, 16); font("normal", 10); center("Certificate under section 203 of the Income-tax Act, 1961 for Tax deducted at source on Salary", y + 12); y += 20;
// Name/address boxes using METADATA and meta.name (employee)
const nameGridH = 50;
const nameGridMidX = LM + (CW / 2);
doc.setLineWidth(0.5);
doc.rect(LM - 5, y, CW + 10, nameGridH);
doc.line(nameGridMidX, y, nameGridMidX, y + nameGridH);
font("normal", 8);
txt("Name and address of the Employer", LM + 6, y + 14);
txt("Name and designation of the employee", nameGridMidX + 6, y + 14);
font("bold", 9);
doc.text(doc.splitTextToSize(METADATA.nameEmp || (meta.ddoOffice || ''), (CW / 2) - 12), LM + 6, y + 30);
doc.text(doc.splitTextToSize(METADATA.nameDesig || (meta.name || ''), (CW / 2) - 12), nameGridMidX + 6, y + 30);
y += nameGridH + 4;
// PAN/TAN/PAN employee row
const panGridH = 40;
const panGridCol1 = LM + 105;
const panGridCol3 = LM + 400;
doc.rect(LM - 5, y, CW + 10, panGridH);
doc.line(panGridCol1, y, panGridCol1, y + panGridH);
doc.line(panGridCol3, y, panGridCol3, y + panGridH);
doc.line(LM - 5, y + 24, RM + 5, y + 24);
font("normal", 8);
txt("PAN no. of the Deductor", LM + 6, y + 12);
txt("TAN No. of the Deductor", panGridCol1 + 6, y + 12);
txt("PAN NO. of the Employee", panGridCol3 + 6, y + 12);
font("bold", 9);
txt(METADATA.panDed || 'PANNOTREQD', LM + 6, y + 36);
txt(METADATA.tanDed || '', panGridCol1 + 6, y + 36);
txt(METADATA.panEmp || '', panGridCol3 + 6, y + 36);
y += panGridH + 4;
// CIT and FY block
const citGridH = 50;
doc.rect(LM - 5, y, CW + 10, citGridH);
doc.line(nameGridMidX, y, nameGridMidX, y + citGridH);
doc.line(nameGridMidX, y + 30, RM + 5, y + 30);
font("normal", 8);
txt("CIT (TDS)", LM + 6, y + 12);
doc.text(doc.splitTextToSize(METADATA.city || "The Commissioner of Income Tax (TDS)\nRoom No. 411, Income Tax Towers, 10-2-3 A.C. Guard.", (CW / 2) - 12), LM + 6, y + 26);
txt("FINANCE YEAR", nameGridMidX + 6, y + 12);
txt("Period", nameGridMidX + 110, y + 12);
font("bold", 9);
txt(METADATA.fy || '2025-26', nameGridMidX + 6, y + 26);
font("normal", 8);
txt("Assessment Year", nameGridMidX + 6, y + 38);
font("bold", 9);
txt(METADATA.ay || '2026-27', nameGridMidX + 6, y + 50);
txt(METADATA.periodFrom || '01/04/2025', nameGridMidX + 110, y + 50);
txt(METADATA.periodTo || '31/03/2026', nameGridMidX + 190, y + 50);
y += citGridH + 6;
// Summary of tax deducted at source - Qtr table
font("normal", 8);
txt("Summary of tax deducted at source", LM + 6, y + 12);
y += 14;
const qtrTblCols = [LM, LM + 50, LM + 180, LM + 320, LM + 450, RM];
const qtrRowH = 18;
doc.rect(LM, y, CW, qtrRowH);
qtrTblCols.forEach((x, i) => { if (i > 0) doc.line(x, y, x, y + qtrRowH + (4 * qtrRowH)); });
font("bold", 7);
txt("Quarter", qtrTblCols[0] + 4, y + 12);
txt("Receipt Numbers of", qtrTblCols[1] + 4, y + 12);
txt("Amount paid / Credited", qtrTblCols[2] + 4, y + 12);
txt("Amount of tax deducted", qtrTblCols[3] + 4, y + 12);
txt("Amount of tax deposited /", qtrTblCols[4] + 4, y + 12);
y += qtrRowH;
font("normal", 8);
txt("remitted in Central Govt.", qtrTblCols[4] + 4, y + 2);
y += 6;
// Use qFromMonthly for paid / deducted / deposited columns.
const totalPaidEach = Math.round((DATA.totalPaid || DATA.gross) / 4);
for (let i = 0; i < 4; i++) {
doc.rect(LM, y, CW, qtrRowH);
font("normal", 8);
txt(`Q${i + 1}`, qtrTblCols[0] + 6, y + 12);
txt("-", qtrTblCols[1] + 6, y + 12);
right(fmt(totalPaidEach), qtrTblCols[3] - 6, y + 12);
right(fmt(qFromMonthly[i] || 0), qtrTblCols[4] - 6, y + 12);
right(fmt(qFromMonthly[i] || 0), RM - 6, y + 12);
y += qtrRowH;
}
doc.rect(LM, y, CW, qtrRowH);
font("bold", 8);
txt("Total", qtrTblCols[0] + 6, y + 12);
right(fmt(DATA.totalPaid || DATA.gross), qtrTblCols[3] - 6, y + 12);
right(fmt(DATA.totalTDS || 0), qtrTblCols[4] - 6, y + 12);
right(fmt(DATA.totalTDS || 0), RM - 6, y + 12);
y += qtrRowH + SECTION_SPACING;
// PART B - Salary Details (mirror existing structure)
font("bold", 11);
txt("PART-B (Refer Note 1)", LM + 6, y + 10);
y += 12;
font("normal", 8);
txt("Details of Salary paid and any other income and tax deducted", LM + 6, y + 8);
y += 14;
// Salary Block
const salaryBlockY = y;
// S.No 1
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("1", LM + 6, y + 10);
txt("Gross Salary", LM + 30, y + 10);
y += ROW_H_M;
// (a) salary as per sec17(1)
doc.rect(LM, y, CW, ROW_H_M);
font("normal", 8);
txt("(a) Salary as per provisions contained in section 17(1)", LM + 30, y + 10);
right(fmt(DATA.gross), RM - 6, y + 10);
y += ROW_H_M;
// (b),(c)
doc.rect(LM, y, CW, ROW_H_M);
txt("(b) Value of perquisites under section 17(2)", LM + 30, y + 10);
right("0", RM - 6, y + 10);
y += ROW_H_M;
doc.rect(LM, y, CW, ROW_H_M);
txt("(c) Profits in lieu of salary under section 17(3)", LM + 30, y + 10);
right("0", RM - 6, y + 10);
y += ROW_H_M;
// TOTAL (a+b+c)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("TOTAL (a+b+c)", LM + 30, y + 10);
right(fmt(DATA.gross), RM - 6, y + 10);
y += ROW_H_M;
// S.No 2 Exemptions u/s 10
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("2", LM + 6, y + 10);
txt("Less: Exempted Allowance u/s 10:", LM + 30, y + 10);
y += ROW_H_M;
// HRA; in NEW regime it will be zero as DATA.hraExemption
doc.rect(LM, y, CW, ROW_H_M);
font("normal", 8);
txt("A) House Rent Allowance vis 10 (2A)13(A) (Exempted only in Old Tax)", LM + 30, y + 10);
right(fmt(DATA.hraExemption), RM - 6, y + 10);
y += ROW_H_M;
doc.rect(LM, y, CW, ROW_H_M);
txt("B) Interest On Housing Loan U/S 24 (B)", LM + 30, y + 10);
right(fmt(isNew ? 0 : DATA.housingLoanInterest || 0), RM - 6, y + 10);
y += ROW_H_M;
// S.No 3 Balance (1-2)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("3", LM + 6, y + 10);
txt("Balance (1-2)", LM + 30, y + 10);
const balanceVal = balance;
right(fmt(balanceVal), RM - 6, y + 10);
y += ROW_H_M;
// Deductions U/s 16
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("4", LM + 6, y + 10);
txt("Deductions U/s 16:", LM + 30, y + 10);
y += ROW_H_M;
// 4(a) Standard Deduction
doc.rect(LM, y, CW, ROW_H_M);
font("normal", 8);
txt("(a) Standard Deduction U/s 16(ia) (Common in New/Old Tax)", LM + 30, y + 10);
right(fmt(DATA.standardDeduction), RM - 6, y + 10);
y += ROW_H_M;
// 4(b) Professional Tax (only in old)
doc.rect(LM, y, CW, ROW_H_M);
txt("(b) Professional Tax U/s 16(iii) (Only in Old Tax)", LM + 30, y + 10);
right(fmt(isNew ? 0 : DATA.pt), RM - 6, y + 10);
y += ROW_H_M;
// 4(c) Entertainment Allowance
doc.rect(LM, y, CW, ROW_H_M);
txt("(c) Entertainment Allowance U/s 16(ii)", LM + 30, y + 10);
right("0", RM - 6, y + 10);
y += ROW_H_M;
// 5 Aggregate (4a to c)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("5", LM + 6, y + 10);
txt("Aggregate of 4(a) to (c)", LM + 30, y + 10);
right(fmt(totalDeductionsU16), RM - 6, y + 10);
y += ROW_H_M;
// 6 Income chargeable under salaries (3-5)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("6", LM + 6, y + 10);
txt("Income chargeable under the head 'salaries' (3-5)", LM + 30, y + 10);
right(fmt(incSal), RM - 6, y + 10);
y += ROW_H_M;
// 7 Other income reported by employee (we assume zero)
doc.rect(LM, y, CW, ROW_H_M);
font("normal", 8);
txt("7", LM + 6, y + 10);
txt("Add: Any other income reported by the employee", LM + 30, y + 10);
right("0", RM - 6, y + 10);
y += ROW_H_M;
// 8 Gross total income (6+7)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("8", LM + 6, y + 10);
txt("Gross total income (6+7)", LM + 30, y + 10);
right(fmt(gti), RM - 6, y + 10);
y += ROW_H_M + SECTION_SPACING;
// CHAPTER VI-A - 10 -> Savings (80C) only for OLD
font("bold", 10);
txt("9", LM + 6, y + 12);
txt("Deductions under Chapter VIA", LM + 32, y + 12);
y += 16;
// Header row for 10
doc.rect(LM, y, CW, 16);
const xGross = LM + 330;
const xQual = LM + 425;
const xDed = RM - 90;
doc.line(xGross, y, xGross, y + 16);
doc.line(xQual, y, xQual, y + 16);
doc.line(xDed, y, xDed, y + 16);
font("bold", 8);
txt("10", LM + 6, y + 12);
txt("Savings U/s 80CCE (80C, 80CCC and 80CCD (i))", LM + 32, y + 12);
txt("Gross Amount", xGross + 4, y + 12);
txt("Qualifying amount", xQual + 4, y + 12);
txt("Deductible Amount Rs.", xDed + 4, y + 12);
y += 16;
// Section 80C line: Only show actual values when OLD; zero when NEW
doc.rect(LM, y, CW, ROW_H_M);
font("normal", 8);
txt("A) Section 80 C (Only in Old Tax)", LM + 30, y + 10);
right(fmt(isNew ? 0 : DATA.S80C_TotalGross), xGross - 6, y + 10);
right(fmt(isNew ? 0 : S11_TotalSavings), xQual - 6, y + 10);
right(fmt(isNew ? 0 : S11_TotalSavings), xDed - 6, y + 10);
y += ROW_H_M;
// 11 Total Deductible Savings (A+B+C+D)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 9);
txt("11", LM + 6, y + 12);
txt("Total Deductible Savings (A+B+C+D)", LM + 32, y + 12);
right(fmt(isNew ? 0 : S11_TotalSavings), xGross - 6, y + 12);
right(fmt(isNew ? 0 : S11_TotalSavings), xQual - 6, y + 12);
right(fmt(isNew ? 0 : S11_TotalSavings), RM - 6, y + 12);
y += ROW_H_M + 4;
// 12 Taxable income (8 - 11)
doc.rect(LM, y, CW, ROW_H_M);
doc.line(xDed, y, xDed, y + ROW_H_M);
font("bold", 9);
txt("12", LM + 6, y + 12);
txt("Taxable income (8 - 11)", LM + 32, y + 12);
txt("Rs:", RM - 18, y + 12);
right(fmt(taxableIncome_S12), RM - 6, y + 12);
y += ROW_H_M + 8;
// PAGE BREAK if needed - for simplicity we do addPage after the previous block
doc.addPage();
y = 30;
// Other Sections 80D, 80E etc (Only Old regime values shown)
font("bold", 9);
txt("13", LM + 6, y + 12);
txt("Other Sections (80CCG, 80D, 80DDB, 80E, 80G etc) under Chapter VI-A (Only in Old Tax)", LM + 32, y + 12);
y += 16;
const xG_O = LM + 330;
const xQ_O = LM + 425;
const xD_O = RM - 90;
doc.rect(LM, y, CW, 16);
doc.line(xG_O, y, xG_O, y + 16);
doc.line(xQ_O, y, xQ_O, y + 16);
doc.line(xD_O, y, xD_O, y + 16);
font("bold", 8);
txt("Gross Amount", xG_O + 6, y + 12);
txt("Qualify amount", xQ_O + 6, y + 12);
txt("Deductible Amount Rs.", xD_O + 6, y + 12);
y += 16;
// a) EWF & relief
doc.rect(LM, y, CW, ROW_H_M);
font("normal", 8);
txt("a) E.W.F, & S.W.F, Kerala flood, Titli relief fund", LM + 32, y + 10);
right(fmt(isNew ? 0 : DATA.S13_EWF_Gross), xG_O - 6, y + 10);
right(fmt(isNew ? 0 : DATA.S13_EWF_Deductible), xQ_O - 6, y + 10);
y += ROW_H_M;
// b) 80D
doc.rect(LM, y, CW, ROW_H_M);
txt("b) 80D-Medical Insurance Premium-Self, Spouse & Children", LM + 32, y + 10);
right(fmt(isNew ? 0 : DATA.S13_80D_Gross), xG_O - 6, y + 10);
right(fmt(isNew ? 0 : DATA.S13_80D_Deductible), xQ_O - 6, y + 10);
y += ROW_H_M;
// c-e others (we show zeros unless data present in S.totals)
const others = ["80E-Interest on Educational Loan","80G-Donation to certain funds","Others"];
others.forEach((label) => {
doc.rect(LM, y, CW, ROW_H_M);
txt(label, LM + 32, y + 10);
right(fmt(0), xG_O - 6, y + 10);
right(fmt(0), xQ_O - 6, y + 10);
y += ROW_H_M;
});
// TOTAL (a+b+c+d+e)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 8);
txt("TOTAL (a+b+c+d+e)", LM + 32, y + 12);
txt("Rs:", xG_O - 18, y + 12);
right(fmt(S13_OtherTotalGross), xG_O - 6, y + 12);
txt("Rs:", xQ_O - 18, y + 12);
right(fmt(aggregateDeductibleS13), xQ_O - 6, y + 12);
y += ROW_H_M + 6;
// Aggregate of deductible amount under Chap. VIA (11+13)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 9);
txt("14", LM + 6, y + 12);
txt("Aggregate of deductible amount under Chap. VIA (11+13)", LM + 32, y + 12);
txt("Rs:", RM - 18, y + 12);
right(fmt(S14_TotalDeductions), RM - 6, y + 12);
y += ROW_H_M + 8;
// TAX BLOCK (15..22)
const taxBlockY = y;
const taxRow = (sl, d, v, boldRow=false) => {
doc.rect(LM, y, CW, ROW_H_M);
font(boldRow ? "bold" : "normal", 8);
const textY = y + 12;
if (sl) txt(sl, LM + 6, textY);
doc.text(doc.splitTextToSize(d, RM - 120 - LM), LM + 28, textY);
if (v !== null && v !== undefined) { txt("Rs:", RM - 30, textY); right(fmt(v), RM - 6, textY); }
y += ROW_H_M;
};
taxRow("15", "Total Income (8 - 14)", totalIncome, true);
taxRow("16", "Tax on total income", taxOnIncome, false);
taxRow("17", "Rebate U/S 87A (Old Tax: <=5Lakh, New Tax: <=7Lakh)", rebate || 0, false);
taxRow("18", "Surcharge", 0, false);
taxRow("19", `Education & health cess @4% on (Tax after Rebate x 4%)`, cess, false);
taxRow("20", "Tax payable (After Rebate & Cess, Rounded off)", taxPayableRounded, true);
taxRow("21", "Less Relief U/s 89(1) (attach details)", 0, false);
taxRow("22", `${DATA.regimeLabel} Total Tax Payable (Rounded off)`, taxPayableFinal, true);
// Less TDS (23)
doc.rect(LM, y, CW, ROW_H_M);
font("normal", 8);
txt("23", LM + 6, y + 12);
txt("Less:(a) Tax deducted at source U/s 192(1)", LM + 28, y + 12);
doc.line(RM - 110, y, RM - 110, y + ROW_H_M);
txt("Rs:", RM - 130, y + 12);
right(fmt(DATA.totalTDS || 0), RM - 6, y + 12);
y += ROW_H_M + 6;
// (b) tax paid by employer on behalf - left as is
doc.rect(LM, y, CW, ROW_H_M);
doc.line(RM - 110, y, RM - 110, y + ROW_H_M);
font("normal", 8);
txt(" (b)", LM + 28, y + 12);
doc.text("Tax paid by the employer on behalf of the employee U/s 192 (1A) on perquisites U/s 17 (2)", LM + 48, y + 12);
y += ROW_H_M + 6;
// TAX PAYABLE / REFUNDABLE (24)
doc.rect(LM, y, CW, ROW_H_M);
font("bold", 9);
txt("24", LM + 6, y + 12);
txt("TAX PAYABLE / REFUNDABLE (22-23)", LM + 28, y + 12);
txt("Rs:", RM - 30, y + 12);
right(fmt(taxPayableResult), RM - 6, y + 12);
y += ROW_H_M + 8;
// Verification
font("bold", 11);
txt("Verification", LM + 6, y + 12);
y += 16;
font("normal", 9);
const empShort = (METADATA.nameDesig || "").split('\n')[0] || METADATA.nameDesig;
const empCapacity = (METADATA.nameDesig || "").split('\n')[1] || "";
const verifText = `I Sri/Smt.: ${empShort.replace(/^Sri\.\s*/i, '')}, Son/Daughter of Sri/smt. working in the capacity of ${empCapacity.trim() || ''}, do hereby certify that the sum of Rs: ${fmt(verifiedAmount)} /- has been deducted and deposited to the credit of the Central Government as per ${DATA.regimeLabel}.`;
doc.text(doc.splitTextToSize(verifText, CW), LM + 6, y);
y += 30;
// Signature boxes and place/date
doc.rect(LM, y, CW / 2 - 6, 50);
doc.rect(LM + CW / 2 + 6, y, CW / 2 - 6, 50);
font("normal", 9);
txt("Place:-", LM + 10, y + 14);
txt(METADATA.city, LM + 80, y + 14);
txt("Date:-", LM + 10, y + 30);
txt(METADATA.date || METADATA.periodTo || "31/03/2026", LM + 80, y + 30);
txt("Designation:", LM + 10, y + 46);
doc.text(doc.splitTextToSize(METADATA.nameEmp.split('\n')[1] || METADATA.nameDesig, 180), LM + 90, y + 46);
right("Signature of person responsible for deduction of tax", RM - 6, y + 26);
right(`Full Name: ${METADATA.nameEmp.split('\n')[0] || METADATA.nameEmp}`, RM - 6, y + 46);
y += 64;
// ANNEXURE A (Book Entry) simplified mapping -- copy your layout
font("bold", 10);
center("ANNEXURE A (Book Entry)", y);
y += 14;
const ac = [LM, LM + 40, LM + 170, LM + 270, LM + 400, RM];
const aRowH = 14;
doc.rect(LM, y, CW, aRowH);
ac.forEach((x, i) => { if (i > 0) doc.line(x, y, x, y + aRowH + (10 * aRowH)); });
font("bold", 7);
txt("Sl.No", ac[0] + 6, y + 10);
doc.text("Tax Deposited in respect\non of the employee (Rs.)", ac[1] + 6, y + 6);
doc.text("Receipt Numbers of Form No 24G", ac[2] + 6, y + 10);
doc.text("DDO Sequence Number in the\nBook Adjustment Mini Statement", ac[3] + 6, y + 6);
doc.text("Date on which tax deposited\n(dd/mm/yy)", ac[4] + 6, y + 6);
y += aRowH;
for (let i = 1; i <= 12; i++) {
doc.rect(LM, y, CW, aRowH);
font("normal", 8);
txt(`${i}.`, ac[0] + 6, y + 10);
if (i === 12) {
right(fmt(annexureA_Total || 0), ac[2] - 6, y + 10);
txt("0", ac[3] + 6, y + 10);
txt("0", ac[4] + 6, y + 10);
txt(METADATA.periodTo || "31/03/2026", ac[4] + 60, y + 10);
} else {
right("0", ac[2] - 6, y + 10);
txt("-", ac[3] + 6, y + 10);
txt("-", ac[4] + 6, y + 10);
txt("-", ac[4] + 60, y + 10);
}
y += aRowH;
}
doc.rect(LM, y, CW, aRowH);
font("bold", 8);
txt("Total", ac[0] + 6, y + 10);
right(fmt(annexureA_Total), ac[2] - 6, y + 10);
y += aRowH + 10;
// ANNEXURE B (Challan) - produce 4 quarter rows with amounts from qFromMonthly
font("bold", 10);
center("ANNEXURE B (Challan)", y);
y += 14;
const bc = [LM, LM + 40, LM + 170, LM + 270, LM + 390, RM];
const bRowH = 14;
doc.rect(LM, y, CW, bRowH);
bc.forEach((x, i) => { if (i > 0) doc.line(x, y, x, y + bRowH + (4 * bRowH)); });
font("bold", 7);
txt("Sl.No", bc[0] + 6, y + 10);
doc.text("Challan Serial No", bc[1] + 6, y + 10);
doc.text("BSR Code of Bank Branch", bc[2] + 6, y + 10);
doc.text("Date of Deposit\n(dd/mm/yy)", bc[3] + 6, y + 6);
doc.text("Amount of Tax Deposited (Rs.)", bc[4] + 6, y + 10);
y += bRowH;
for (let i = 0; i < 4; i++) {
doc.rect(LM, y, CW, bRowH);
font("normal", 8);
txt(`${i + 1}.`, bc[0] + 6, y + 10);
txt(`${i + 1}`, bc[1] + 6, y + 10);
txt("0000000", bc[2] + 6, y + 10);
const yr = (METADATA.periodTo || "31/03/2026").split('/').pop() || "2026";
txt(`31/03/${yr}`, bc[3] + 6, y + 10);
right(fmt(qFromMonthly[i] || 0), RM - 6, y + 10);
y += bRowH;
}
doc.rect(LM, y, CW, bRowH);
font("bold", 8);
txt("Total", bc[0] + 6, y + 10);
right(fmt(DATA.totalTDS || 0), RM - 6, y + 10);
y += bRowH + 10;
// Footer credit
font("normal", 8);
center(METADATA.footer || "Designed By: K.SANYASI NAIDU, SGT, VISAKHAPATNAM", y);
// Double border per-page (like your existing code)
const pages = doc.internal.getNumberOfPages();
for (let i = 1; i <= pages; i++) {
doc.setPage(i);
doc.setLineWidth(0.6);
doc.rect(LM - 5, 20, CW + 10, PG_H - 40);
doc.setLineWidth(0.4);
doc.rect(LM - 2, 23, CW + 4, PG_H - 46);
}
// Save file name using employee short name
const empLabelRaw = (METADATA.nameDesig || METADATA.nameEmp || 'Employee').toString();
const empLabel = empLabelRaw.replace(/\s+/g,'_').replace(/[^\w\-_.]/g,'');
doc.save(`Form16_${isNew ? 'NEW' : 'OLD'}_${empLabel || 'Employee'}.pdf`);
} catch (err) {
console.error('Form16 generation error', err);
alert('Error generating Form16 PDF. Check browser console for details.');
}
}
0 Comments:
Post a Comment