Property Management Income & Expense Tracker

Property Management Tracker

Manage properties, track income and expenses, and view financial reports.

Net Operating Income (YTD)

$0

Total Income (YTD)

$0

Total Expenses (YTD)

$0

Property Name Address Actions
Date Property Category Amount Actions

Year: ${year === 'all' ? 'All Years' : year}

Income

${Object.entries(incomeByCategory).map(([cat, amt]) => `
${cat}${formatCurrency(amt)}
`).join('')}
Total Income${formatCurrency(income)}

Expenses

${Object.entries(expensesByCategory).map(([cat, amt]) => `
${cat}${formatCurrency(amt)}
`).join('')}
Total Expenses${formatCurrency(expenses)}

Net Operating Income ${formatCurrency(noi)}
`; elements.reportOutput.innerHTML = reportHTML; }; const downloadReportPdf = () => { const reportContent = document.getElementById('pdf-report-content'); if (!reportContent) { alert('Please generate a report first.'); return; } const { jsPDF } = window.jspdf; html2canvas(reportContent, { scale: 2 }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasWidth / canvasHeight; let imgWidth = pdfWidth - 20; let imgHeight = imgWidth / ratio; if (imgHeight > pdfHeight - 20) { imgHeight = pdfHeight - 20; imgWidth = imgHeight * ratio; } const x = (pdfWidth - imgWidth) / 2; pdf.addImage(imgData, 'PNG', x, 10, imgWidth, imgHeight); pdf.save('Property-Report.pdf'); }); }; // --- EVENT LISTENERS --- elements.tabs.forEach((tab, index) => tab.addEventListener('click', () => { currentTab = index; elements.tabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); elements.tabContents.forEach(c => c.classList.remove('active')); elements.tabContents[index].classList.add('active'); })); elements.addPropertyBtn.addEventListener('click', () => { elements.propertyForm.reset(); document.getElementById('property-id').value = ''; document.getElementById('property-modal-title').textContent = 'Add Property'; showModal(elements.propertyModal); }); elements.propertiesTableBody.addEventListener('click', (e) => { const id = e.target.dataset.id; if (e.target.classList.contains('edit-property-btn')) { const property = db.properties.find(p => p.id == id); document.getElementById('property-id').value = property.id; document.getElementById('property-name').value = property.name; document.getElementById('property-address').value = property.address; document.getElementById('property-modal-title').textContent = 'Edit Property'; showModal(elements.propertyModal); } if (e.target.classList.contains('delete-property-btn')) { if (confirm('Are you sure? This will also delete all associated transactions.')) { db.properties = db.properties.filter(p => p.id != id); db.transactions = db.transactions.filter(t => t.propertyId != id); saveToLocalStorage(); renderProperties(); renderTransactions(); renderDashboard(); } } }); elements.propertyForm.addEventListener('submit', handlePropertyFormSubmit); document.querySelectorAll('.modal-cancel-btn').forEach(btn => btn.addEventListener('click', () => { hideModal(elements.propertyModal); hideModal(elements.transactionModal); })); elements.addIncomeBtn.addEventListener('click', () => openTransactionModal('income')); elements.addExpenseBtn.addEventListener('click', () => openTransactionModal('expense')); elements.transactionForm.addEventListener('submit', handleTransactionFormSubmit); elements.transactionsTableBody.addEventListener('click', (e) => { const id = e.target.dataset.id; if (e.target.classList.contains('edit-transaction-btn')) { const transaction = db.transactions.find(t => t.id == id); openTransactionModal(transaction.type, transaction); } if (e.target.classList.contains('delete-transaction-btn')) { if (confirm('Are you sure you want to delete this transaction?')) { db.transactions = db.transactions.filter(t => t.id != id); saveToLocalStorage(); renderTransactions(); renderDashboard(); } } }); elements.generateReportBtn.addEventListener('click', generateReport); elements.downloadPdfBtn.addEventListener('click', downloadReportPdf); // --- INITIALIZATION --- if (db.properties.length === 0) { // Add sample data if empty db.properties = [ { id: 1, name: 'Maple Street Duplex', address: '123 Maple St, Anytown, USA' }, { id: 2, name: 'Oak Avenue Condo', address: '456 Oak Ave, Anytown, USA' } ]; db.transactions = [ { id: 1, type: 'income', date: `${new Date().getFullYear()}-01-05`, propertyId: 1, category: 'Rent', amount: 1800 }, { id: 2, type: 'expense', date: `${new Date().getFullYear()}-01-10`, propertyId: 1, category: 'Repairs', amount: 250 }, { id: 3, type: 'income', date: `${new Date().getFullYear()}-02-05`, propertyId: 2, category: 'Rent', amount: 1200 }, ]; saveToLocalStorage(); } renderProperties(); renderTransactions(); renderDashboard(); populateYearSelect(); generateReport(); });
Scroll to Top