Tax Savings From Long-Term
$${taxResults.savings.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
Tax Breakdown by Gain Type
| Gain Type | Total Gain | Tax Owed |
|---|---|---|
| Short-Term Gains | $${taxResults.shortTerm.gain.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})} | $${taxResults.shortTerm.tax.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})} |
| Long-Term Gains | $${taxResults.longTerm.gain.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})} | $${taxResults.longTerm.tax.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})} |
$
${investmentForms}
`;
};
// --- DATA MANIPULATION & EVENT HANDLERS ---
window.updateProfile = (key, value) => userProfile[key] = (key === 'taxableIncome') ? parseFloat(value) || 0 : value;
window.addInvestment = () => {
const today = new Date().toISOString().split('T')[0];
investments.push({ id: Date.now(), description: 'New Investment', purchasePrice: 0, salePrice: 0, purchaseDate: today, saleDate: today });
renderCurrentTab();
};
window.updateInvestment = (id, key, value) => {
const inv = investments.find(i => i.id === id);
if(inv) inv[key] = (key.includes('Price')) ? parseFloat(value) || 0 : value;
};
window.deleteInvestment = (id) => {
investments = investments.filter(i => i.id !== id);
renderCurrentTab();
};
// --- TAX CALCULATION LOGIC ---
const calculateTax = (income, brackets) => {
let tax = 0;
let remainingIncome = income;
let prevMax = 0;
for (const bracket of brackets) {
if (remainingIncome <= 0) break;
const taxableInBracket = Math.min(remainingIncome, bracket.max - prevMax);
tax += taxableInBracket * bracket.rate;
remainingIncome -= taxableInBracket;
prevMax = bracket.max;
}
return tax;
};
const handleCalculation = () => {
const status = userProfile.filingStatus;
const income = userProfile.taxableIncome;
let totalShortTermGain = 0;
let totalLongTermGain = 0;
investments.forEach(inv => {
const gain = inv.salePrice - inv.purchasePrice;
if (gain <= 0) return;
const holdingPeriod = (new Date(inv.saleDate) - new Date(inv.purchaseDate)) / (1000 * 60 * 60 * 24);
if (holdingPeriod > 365) {
totalLongTermGain += gain;
} else {
totalShortTermGain += gain;
}
});
// 1. Calculate tax on Long-Term Gains
const longTermBrackets = taxData.longTerm[status];
let taxOnLongTerm = 0;
let remainingLongTermGain = totalLongTermGain;
let incomeLevel = income;
for (const bracket of longTermBrackets) {
if (remainingLongTermGain <= 0) break;
const bracketRoom = bracket.max - incomeLevel;
if (bracketRoom > 0) {
const taxableInBracket = Math.min(remainingLongTermGain, bracketRoom);
taxOnLongTerm += taxableInBracket * bracket.rate;
remainingLongTermGain -= taxableInBracket;
}
incomeLevel = bracket.max; // Move to next bracket threshold
}
// 2. Calculate tax on Short-Term Gains
const ordinaryBrackets = taxData.ordinary[status];
const taxOnBaseIncome = calculateTax(income, ordinaryBrackets);
const taxOnTotalIncome = calculateTax(income + totalShortTermGain, ordinaryBrackets);
const taxOnShortTerm = taxOnTotalIncome - taxOnBaseIncome;
// 3. Calculate potential savings
const taxIfAllShortTerm = calculateTax(income + totalShortTermGain + totalLongTermGain, ordinaryBrackets) - taxOnBaseIncome;
const savings = taxIfAllShortTerm - (taxOnShortTerm + taxOnLongTerm);
taxResults = {
shortTerm: { gain: totalShortTermGain, tax: taxOnShortTerm },
longTerm: { gain: totalLongTermGain, tax: taxOnLongTerm },
totalTax: taxOnShortTerm + taxOnLongTerm,
effectiveRate: (totalShortTermGain + totalLongTermGain > 0) ? ((taxOnShortTerm + taxOnLongTerm) / (totalShortTermGain + totalLongTermGain)) * 100 : 0,
savings: savings > 0 ? savings : 0
};
switchTab(0);
};
// --- CHART & PDF ---
const renderChart = () => {
if (taxChart) taxChart.destroy();
const ctx = document.getElementById('taxChart')?.getContext('2d');
if (!ctx || !taxResults) return;
taxChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ['Short-Term Gains Tax', 'Long-Term Gains Tax'],
datasets: [{
data: [taxResults.shortTerm.tax, taxResults.longTerm.tax],
backgroundColor: ['#ef4444', '#10b981'], // Red, Green
}]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Tax Apportionment by Gain Type' } } }
});
};
window.downloadPDF = () => {
if (!taxResults) return;
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.setFontSize(20);
doc.text("Investment Gains Tax Report", doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' });
doc.setFontSize(11);
doc.text(`Based on ${userProfile.filingStatus} status and $${userProfile.taxableIncome.toLocaleString()} income.`, doc.internal.pageSize.getWidth() / 2, 28, { align: 'center' });
const summaryText = `
Total Tax on Gains: $${taxResults.totalTax.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
Tax Savings from Long-Term Gains: $${taxResults.savings.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
Effective Tax Rate on Gains: ${taxResults.effectiveRate.toFixed(2)}%
`;
doc.text(summaryText, 14, 45);
const finalY = taxChart ? 75 : 45;
if(taxChart) {
const chartImage = taxChart.toBase64Image();
doc.addImage(chartImage, 'PNG', 14, finalY, 180, 80);
}
const head = [['Description', 'Gain/Loss ($)', 'Holding Period', 'Gain Type', 'Estimated Tax ($)']];
const body = investments.map(inv => {
const gain = inv.salePrice - inv.purchasePrice;
const pDate = new Date(inv.purchaseDate);
const sDate = new Date(inv.saleDate);
const holdingDays = Math.round((sDate - pDate) / (1000 * 60 * 60 * 24));
const type = holdingDays > 365 ? 'Long-Term' : 'Short-Term';
// This is a simplified tax attribution for the PDF, not the precise calculation
const estTax = type === 'Long-Term' ? (gain / taxResults.longTerm.gain) * taxResults.longTerm.tax : (gain / taxResults.shortTerm.gain) * taxResults.shortTerm.tax;
return [
inv.description,
gain.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}),
`${holdingDays} days`,
type,
gain > 0 ? (isNaN(estTax) ? 0 : estTax).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : 'N/A'
];
});
doc.autoTable({ startY: finalY + (taxChart ? 90 : 10), head: head, body: body, theme: 'striped', headStyles: { fillColor: [5, 150, 105] } });
doc.save('Tax-Optimization-Report.pdf');
};
// --- INITIALIZATION ---
DOM.nextBtn.addEventListener('click', () => switchTab(currentTab + 1));
DOM.prevBtn.addEventListener('click', () => switchTab(currentTab - 1));
DOM.tabButtons.forEach((btn, i) => btn.addEventListener('click', () => switchTab(i)));
renderCurrentTab();
updateNavButtons();
});
