Property Management Report Generator

Property Management Report Generator

Financial Inputs (USD)

Occupancy Metrics

Summarize the leasing activity and market outlook.

Maintenance & Capital Spending

List any significant issues and their resolution status.

A high-level summary for the owner on the property's health and outlook.

Review the generated report below before downloading.

Click "Next" or "Previous" to refresh this preview.

Total Repair/Maintenance Spend: ${formatCurrency(data.repairSpend)}

Total Capital Spend: ${formatCurrency(data.capitalSpend)}

Major Issues:

${formatNarrative(data.majorIssues)}

Overall Narrative & Manager's Summary

${formatNarrative(data.overallNarrative)}

Prepared by: ${escapeHTML(data.projectManager || 'Property Manager')}

`; }; // --- Download Functions --- const getTxtContent = () => { const data = getReportData(); let content = `PROPERTY MANAGEMENT REPORT: ${data.reportName}\n`; content += `----------------------------------------\n`; content += `Property: ${data.propertyAddress}\n`; content += `Period: ${data.reportPeriod}\n`; content += `Manager: ${data.projectManager}\n\n`; content += `1. FINANCIAL SUMMARY (USD)\n`; content += `----------------------------------------\n`; content += `Gross Rental Income & Other: ${formatCurrency(data.grossIncome)}\n`; content += `Total Operating Expenses: -${formatCurrency(data.totalOpEx).substring(1)}\n`; content += `Net Operating Income (NOI): ${formatCurrency(data.noi)}\n`; content += `Less Reserves/Debt Service: -${formatCurrency(data.reserves).substring(1)}\n`; content += `NET CASH FLOW: ${formatCurrency(data.cashFlow)}\n\n`; content += `2. OCCUPANCY & LEASING\n`; content += `----------------------------------------\n`; content += `Total Units: ${data.totalUnits}\n`; content += `Occupied Units: ${data.occupiedUnits}\n`; content += `Occupancy Rate: ${data.occupancyRate}%\n`; content += `New Leases Signed: ${data.newLeases}\n`; content += `Avg. Days Vacant: ${data.avgVacantDays} days\n\n`; content += `LEASING COMMENTARY:\n${data.leasingCommentary}\n\n`; content += `3. MAINTENANCE & HEALTH\n`; content += `----------------------------------------\n`; content += `Total Repair Spend: ${formatCurrency(data.repairSpend)}\n`; content += `Total Capital Spend: ${formatCurrency(data.capitalSpend)}\n\n`; content += `MAJOR ISSUES:\n${data.majorIssues}\n\n`; content += `4. OVERALL NARRATIVE\n`; content += `----------------------------------------\n`; content += `${data.overallNarrative}\n\n`; content += `Prepared by: ${data.projectManager}\n`; return content; }; const downloadTxt = () => { const txtContent = getTxtContent(); const blob = new Blob([txtContent], { type: 'text/plain;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `${inputs.reportName.value.replace(/ /g, '_') || 'property_report'}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }; const downloadPDF = () => { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { showMessage('Error: jsPDF library not loaded.', 'error'); return; } if (typeof window.jspdf.autoTable === 'undefined') { showMessage('Error: jsPDF-AutoTable library not loaded.', 'error'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const data = getReportData(); const margin = 15; const pageWidth = doc.internal.pageSize.getWidth(); const usableWidth = pageWidth - (margin * 2); let yPos = 15; // Helper function for narrative text const addNarrative = (text, startY, isBold = false) => { doc.setFontSize(10); doc.setFont(undefined, isBold ? 'bold' : 'normal'); const splitText = doc.splitTextToSize(text, usableWidth); if (yPos + (splitText.length * 5) > 280) { doc.addPage(); yPos = 15; } doc.text(splitText, margin, startY); return startY + (splitText.length * 5) + 3; }; // 1. HEADER doc.setFontSize(18); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text(data.reportName || 'PROPERTY REPORT', pageWidth / 2, yPos, { align: 'center' }); yPos += 8; doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.setTextColor(108, 117, 125); doc.text(`${data.propertyAddress} | Period: ${data.reportPeriod}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 10; // 2. FINANCIAL SUMMARY doc.setFontSize(13); doc.setFont(undefined, 'bold'); doc.setTextColor(0, 123, 255); doc.text('1. Financial Summary (USD)', margin, yPos); yPos += 2; const financialBody = [ ['Gross Rental Income & Other', formatCurrency(data.grossIncome)], ['Total Operating Expenses', formatCurrency(data.totalOpEx)], ['Net Operating Income (NOI)', formatCurrency(data.noi)], ['Less: Capital Reserves / Debt Service', formatCurrency(data.reserves)], ['NET CASH FLOW', formatCurrency(data.cashFlow)] ]; doc.autoTable({ startY: yPos + 2, head: [], body: financialBody, theme: 'grid', styles: { fontSize: 10, cellPadding: 2, valign: 'middle' }, headStyles: { fillColor: [248, 249, 250] }, columnStyles: { 0: { fontStyle: 'bold', fillColor: [248, 249, 250], cellWidth: 80 }, 1: { cellWidth: 'auto', halign: 'right' } } }); yPos = doc.autoTable.previous.finalY + 5; // 3. OCCUPANCY doc.setFontSize(13); doc.setFont(undefined, 'bold'); doc.setTextColor(0, 123, 255); doc.text('2. Occupancy & Leasing Overview', margin, yPos); yPos += 2; const occupancyBody = [ ['Total Units', data.totalUnits, 'Occupancy Rate', `${data.occupancyRate}%`], ['Occupied Units', data.occupiedUnits, 'New Leases Signed', data.newLeases], ['Vacant Units', data.vacantUnits, 'Avg. Days Vacant', `${data.avgVacantDays} days`] ]; doc.autoTable({ startY: yPos + 2, head: [], body: occupancyBody, theme: 'grid', styles: { fontSize: 10, cellPadding: 2 }, columnStyles: { 0: { fontStyle: 'bold', fillColor: [248, 249, 250] }, 2: { fontStyle: 'bold', fillColor: [248, 249, 250] } } }); yPos = doc.autoTable.previous.finalY + 5; doc.setFontSize(10); doc.setFont(undefined, 'bold'); doc.setTextColor(52, 73, 94); doc.text('Leasing Commentary:', margin, yPos); yPos = addNarrative(data.leasingCommentary, yPos + 5); // 4. MAINTENANCE doc.setFontSize(13); doc.setFont(undefined, 'bold'); doc.setTextColor(0, 123, 255); doc.text('3. Maintenance & Property Health', margin, yPos); yPos += 5; yPos = addNarrative(`Total Repair/Maintenance Spend: ${formatCurrency(data.repairSpend)} | Total Capital Spend: ${formatCurrency(data.capitalSpend)}`, yPos); doc.setFontSize(10); doc.setFont(undefined, 'bold'); doc.setTextColor(52, 73, 94); doc.text('Major Issues:', margin, yPos); yPos = addNarrative(data.majorIssues, yPos + 5); // 5. OVERALL NARRATIVE doc.setFontSize(13); doc.setFont(undefined, 'bold'); doc.setTextColor(0, 123, 255); doc.text('4. Overall Narrative & Summary', margin, yPos); yPos += 5; yPos = addNarrative(data.overallNarrative, yPos); // 6. Signature doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.text(`Prepared by: ${data.projectManager}`, pageWidth - margin, yPos + 10, { align: 'right' }); doc.save(`${data.reportName.replace(/ /g, '_') || 'property_report'}.pdf`); }; // --- Event Listeners --- // Tab Buttons tabButtons.forEach((btn, index) => { btn.addEventListener('click', () => showTab(index + 1)); }); // Next/Prev Navigation nextBtn.addEventListener('click', () => showTab(currentTab + 1)); prevBtn.addEventListener('click', () => showTab(currentTab - 1)); // Tab 4 Actions downloadPdfBtn.addEventListener('click', downloadPDF); downloadTxtBtn.addEventListener('click', downloadTxt); // --- Initialization --- showTab(1); // Set initial state });
Scroll to Top