Work-From-Home Stipend Calculator

Stipend Configuration

USD ($)

All expense amounts should be entered in USD.

Recurring Monthly Expenses

Enter the estimated monthly cost for each item.

Add Custom Recurring Expense

One-Time or Infrequent Equipment/Setup Costs

One-Time/Infrequent Items List

Item NameTotal Cost ($)FrequencyAmort. Period (M)Action

Work-From-Home Stipend Calculation Summary

Recurring Expenses (per Stipend Period)

Expense ItemCost for Period ($)

Total Recurring Expenses: $0.00

One-Time/Infrequent Expenses (for Stipend Period)

Item NameTotal Cost ($)Calculated Cost for Period ($)

Total One-Time/Infrequent Expenses: $0.00

Grand Total Recommended Stipend: $0.00

Employee Name/ID: ${stipendConfig.employeeName}

Stipend Calculation Period: ${stipendConfig.stipendPeriodText}

Currency: USD ($)

`; let monthsInPeriod = 1; if (stipendConfig.stipendPeriod === 'Quarterly') monthsInPeriod = 3; else if (stipendConfig.stipendPeriod === 'Annually') monthsInPeriod = 12; // Recurring Expenses Summary summaryRecurringTableBody.innerHTML = ''; let totalRecurringCostForPeriod = 0; const predefinedRecurringInputs = document.querySelectorAll('.wfhsc-recurring-input'); predefinedRecurringInputs.forEach(input => { const monthlyCost = parseFloat(input.value) || 0; if (monthlyCost > 0) { const costForPeriod = monthlyCost * monthsInPeriod; totalRecurringCostForPeriod += costForPeriod; const row = summaryRecurringTableBody.insertRow(); row.insertCell().textContent = input.dataset.name || input.id; row.insertCell().textContent = costForPeriod.toFixed(2); row.cells[1].style.textAlign = 'right'; } }); customRecurringExpenses.forEach(item => { const costForPeriod = item.cost * monthsInPeriod; totalRecurringCostForPeriod += costForPeriod; const row = summaryRecurringTableBody.insertRow(); row.insertCell().textContent = item.name; row.insertCell().textContent = costForPeriod.toFixed(2); row.cells[1].style.textAlign = 'right'; }); if (summaryRecurringTableBody.rows.length === 0) { const row = summaryRecurringTableBody.insertRow(); const cell = row.insertCell(); cell.colSpan = 2; cell.textContent = 'No recurring expenses entered.'; cell.style.textAlign = 'center'; } totalRecurringSummarySpan.textContent = totalRecurringCostForPeriod.toFixed(2); // One-Time/Infrequent Expenses Summary summaryOneTimeTableBody.innerHTML = ''; let totalOneTimeCostForPeriod = 0; oneTimeExpenses.forEach(item => { let costForPeriod = 0; if (item.frequency === 'oneTimeCurrent') { costForPeriod = item.totalCost; } else if (item.frequency === 'amortize' && item.amortizationMonths > 0) { const monthlyAmortizedCost = item.totalCost / item.amortizationMonths; costForPeriod = monthlyAmortizedCost * monthsInPeriod; } totalOneTimeCostForPeriod += costForPeriod; const row = summaryOneTimeTableBody.insertRow(); row.insertCell().textContent = item.name; row.insertCell().textContent = item.totalCost.toFixed(2); row.cells[1].style.textAlign = 'right'; row.insertCell().textContent = costForPeriod.toFixed(2); row.cells[2].style.textAlign = 'right'; }); if (summaryOneTimeTableBody.rows.length === 0) { const row = summaryOneTimeTableBody.insertRow(); const cell = row.insertCell(); cell.colSpan = 3; cell.textContent = 'No one-time/infrequent expenses entered.'; cell.style.textAlign = 'center'; } totalOneTimeSummarySpan.textContent = totalOneTimeCostForPeriod.toFixed(2); grandTotalStipendSpan.textContent = (totalRecurringCostForPeriod + totalOneTimeCostForPeriod).toFixed(2); } // --- PDF Download --- downloadPdfBtn.addEventListener('click', function() { collectStipendConfig(); // Ensure latest config const { jsPDF } = window.jspdf; const doc = new jsPDF(); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); const accentColor = getComputedStyle(document.documentElement).getPropertyValue('--table-header-bg').trim(); const whiteText = getComputedStyle(document.documentElement).getPropertyValue('--white-text').trim(); const darkText = getComputedStyle(document.documentElement).getPropertyValue('--dark-text').trim(); let YPosition = 15; const leftMargin = 14; const pageHeight = doc.internal.pageSize.height; function checkAndAddPage(currentY) { if (currentY > pageHeight - 30) { doc.addPage(); return 20; } return currentY; } doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text("Work-From-Home Stipend Report", doc.internal.pageSize.getWidth() / 2, YPosition, { align: 'center' }); YPosition += 10; doc.setFontSize(12); doc.setTextColor(darkText); YPosition = checkAndAddPage(YPosition); doc.setFont(undefined, 'bold'); doc.text("Stipend Configuration", leftMargin, YPosition); YPosition += 6; doc.setFont(undefined, 'normal'); const configDetails = [ ["Employee Name/ID:", stipendConfig.employeeName], ["Stipend Period:", stipendConfig.stipendPeriodText], ["Currency:", "USD ($)"] ]; doc.autoTable({ startY: YPosition, body: configDetails, theme: 'plain', styles: { fontSize: 10, cellPadding: 1.5, textColor: darkText }, columnStyles: { 0: { fontStyle: 'bold', cellWidth: 50 } } }); YPosition = doc.lastAutoTable.finalY + 8; let monthsInPeriod = 1; if (stipendConfig.stipendPeriod === 'Quarterly') monthsInPeriod = 3; else if (stipendConfig.stipendPeriod === 'Annually') monthsInPeriod = 12; // Recurring Expenses YPosition = checkAndAddPage(YPosition); doc.setFontSize(12); doc.setFont(undefined, 'bold'); doc.setTextColor(accentColor); doc.text(`Recurring Expenses (for ${stipendConfig.stipendPeriodText} period)`, leftMargin, YPosition); YPosition += 6; const recurringHeaders = [['Expense Item', `Cost for Period ($)`]]; const recurringBody = []; let totalRecurringForPdf = 0; document.querySelectorAll('.wfhsc-recurring-input').forEach(input => { const monthlyCost = parseFloat(input.value) || 0; if (monthlyCost > 0) { const costForPeriod = monthlyCost * monthsInPeriod; recurringBody.push([input.dataset.name || input.id, costForPeriod.toFixed(2)]); totalRecurringForPdf += costForPeriod; } }); customRecurringExpenses.forEach(item => { const costForPeriod = item.cost * monthsInPeriod; recurringBody.push([item.name, costForPeriod.toFixed(2)]); totalRecurringForPdf += costForPeriod; }); if (recurringBody.length > 0) { doc.autoTable({ startY: YPosition, head: recurringHeaders, body: recurringBody, theme: 'grid', headStyles: { fillColor: accentColor, textColor: whiteText, fontSize: 9 }, styles: { fontSize: 8 }, columnStyles: { 1: { halign: 'right' } } }); YPosition = doc.lastAutoTable.finalY; } else { doc.setFontSize(10).setFont(undefined,'normal').text("No recurring expenses.", leftMargin, YPosition); YPosition +=6; } doc.setFontSize(10).setFont(undefined,'bold').text(`Total Recurring Expenses: $${totalRecurringForPdf.toFixed(2)}`, leftMargin, YPosition + 5); YPosition += 12; // One-Time/Infrequent Expenses YPosition = checkAndAddPage(YPosition); doc.setFontSize(12); doc.setFont(undefined, 'bold'); doc.setTextColor(accentColor); doc.text(`One-Time/Infrequent Expenses (cost for ${stipendConfig.stipendPeriodText} period)`, leftMargin, YPosition); YPosition += 6; const oneTimeHeaders = [['Item Name', 'Total Cost ($)', 'Frequency/Amort.', `Cost for Period ($)`]]; const oneTimeBody = []; let totalOneTimeForPdf = 0; oneTimeExpenses.forEach(item => { let costForPeriod = 0; let freqText = "One-Time"; if (item.frequency === 'oneTimeCurrent') { costForPeriod = item.totalCost; } else if (item.frequency === 'amortize' && item.amortizationMonths > 0) { const monthlyAmortizedCost = item.totalCost / item.amortizationMonths; costForPeriod = monthlyAmortizedCost * monthsInPeriod; freqText = `Amort. (${item.amortizationMonths}m)`; } oneTimeBody.push([item.name, item.totalCost.toFixed(2), freqText, costForPeriod.toFixed(2)]); totalOneTimeForPdf += costForPeriod; }); if (oneTimeBody.length > 0) { doc.autoTable({ startY: YPosition, head: oneTimeHeaders, body: oneTimeBody, theme: 'grid', headStyles: { fillColor: accentColor, textColor: whiteText, fontSize: 9 }, styles: { fontSize: 8 }, columnStyles: { 1: { halign: 'right' }, 3: { halign: 'right' } } }); YPosition = doc.lastAutoTable.finalY; } else { doc.setFontSize(10).setFont(undefined,'normal').text("No one-time/infrequent expenses.", leftMargin, YPosition); YPosition +=6; } doc.setFontSize(10).setFont(undefined,'bold').text(`Total One-Time/Infrequent Expenses: $${totalOneTimeForPdf.toFixed(2)}`, leftMargin, YPosition + 5); YPosition += 12; // Grand Total YPosition = checkAndAddPage(YPosition); doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.setTextColor(primaryColor); const grandTotal = totalRecurringForPdf + totalOneTimeForPdf; doc.text(`Grand Total Recommended Stipend (for ${stipendConfig.stipendPeriodText}): $${grandTotal.toFixed(2)}`, leftMargin, YPosition); YPosition += 10; doc.setFontSize(8); doc.setTextColor(100); const generatedDateText = `PDF Generated on: ${REF_DATE.toISOString().split('T')[0]} ${REF_DATE.toLocaleTimeString()}`; doc.text(generatedDateText, leftMargin, doc.internal.pageSize.height - 10); doc.save(`WFH_Stipend_Report_${stipendConfig.employeeName.replace(/\s+/g, '_') || 'Employee'}.pdf`); }); // Initial setup setActiveTab(0); // Start on tab 1 renderCustomRecurringTable(); // Initialize table view renderOneTimeTable(); // Initialize table view });
Scroll to Top