Office Rent & Lease Budget Planner

General Settings

Property & Lease Details

No properties added yet. Click "Add Property/Lease" to begin.

Lease Budget Summary

Complete data in previous tabs to see the summary.

No other recurring costs added for this property.

One-Time Costs

No one-time costs added for this property.

`; propertiesContainer.appendChild(propDiv); setupEscalationVisibility(propDiv); propDiv.querySelector('.orl-remove-property').addEventListener('click', function() { this.closest('.orl-property-section').remove(); checkEmptyState('#orl-properties-container', 'orl-no-items-text'); }); propDiv.querySelectorAll('.orl-add-other-recurring').forEach(btn => btn.addEventListener('click', handleAddSubItem)); propDiv.querySelectorAll('.orl-add-one-time').forEach(btn => btn.addEventListener('click', handleAddSubItem)); propDiv.querySelector(`#prop-area-unit-${propertyIdCounter}`).addEventListener('input', function() { const suffix = this.closest('.orl-property-section').querySelector('.orl-property-area-unit-suffix'); if (suffix) suffix.textContent = this.value || defaultAreaUnitInput.value || 'unit'; }); checkEmptyState('#orl-properties-container', 'orl-no-items-text'); updateDynamicTextElements(); // To apply currency to new elements } function setupEscalationVisibility(parentElement) { parentElement.querySelectorAll('.orl-esc-type-select').forEach(select => { const valueGroup = select.closest('div').parentElement.querySelector('.orl-esc-value-group'); const afterYearGroup = select.closest('div').parentElement.querySelector('.orl-esc-after-year-group'); const escUnitSpan = valueGroup ? valueGroup.querySelector('.orl-esc-unit') : null; function toggleEscFields() { if (select.value === 'None') { if (valueGroup) valueGroup.style.display = 'none'; if (afterYearGroup) afterYearGroup.style.display = 'none'; } else { if (valueGroup) valueGroup.style.display = 'flex'; if (afterYearGroup) afterYearGroup.style.display = 'flex'; if (escUnitSpan) escUnitSpan.textContent = select.value === 'Percentage' ? '%' : (currencySymbolInput.value || '$'); } } select.addEventListener('change', toggleEscFields); toggleEscFields(); // Initial call }); } function handleAddSubItem(event) { const targetContainerId = event.target.dataset.targetContainer; const container = document.getElementById(targetContainerId); const isOneTime = event.target.classList.contains('orl-add-one-time'); const currency = currencySymbolInput.value || '$'; const subItemId = Date.now(); // Unique ID const itemDiv = document.createElement('div'); itemDiv.classList.add('orl-repeatable-item'); if (isOneTime) { itemDiv.innerHTML = `
${currency}
`; } else { // Other Recurring Cost itemDiv.innerHTML = `
${currency}
`; setupEscalationVisibility(itemDiv); // Setup for the new recurring item } container.appendChild(itemDiv); itemDiv.querySelector('.orl-remove-button').addEventListener('click', function() { this.closest('.orl-repeatable-item').remove(); checkEmptyState(`#${container.id}`, 'orl-no-items-text'); }); checkEmptyState(`#${container.id}`, 'orl-no-items-text'); updateDynamicTextElements(); } // --- Summary Generation --- function getNum(value) { return parseFloat(value) || 0; } function calculateAnnualCost(initialAmount, frequency, escalationType, escalationValue, escalationAfterYear, currentYear, area, amountType) { let annualCost = initialAmount; if (amountType === 'PerAreaUnitPerPeriod') { annualCost = initialAmount * area; } if (frequency === 'Monthly') { annualCost *= 12; } if (currentYear > escalationAfterYear && escalationType !== 'None') { const prevYearsToEscalate = currentYear - escalationAfterYear; let effectiveEscalatedAnnualCost = annualCost; // Start with the initial annual cost for escalation base // To correctly calculate compounded escalation, we need the *true* initial annual cost // before any escalations. This is tricky if we only pass currentYear cost. // Let's assume `initialAmount` is the *true* unescalated periodic amount. let baseForEsc = (amountType === 'PerAreaUnitPerPeriod' ? initialAmount * area : initialAmount) * (frequency === 'Monthly' ? 12 : 1); for (let i = 1; i <= prevYearsToEscalate; i++) { if (escalationType === 'Percentage') { baseForEsc *= (1 + getNum(escalationValue) / 100); } else if (escalationType === 'Fixed') { baseForEsc += getNum(escalationValue); // Assuming escalationValue is annual fixed increase } } return baseForEsc; // This is the cost for the currentYear } return annualCost; // If currentYear <= escalationAfterYear or no escalation } function generateLeaseSummary() { const currency = currencySymbolInput.value || '$'; const budgetHorizonYears = parseInt(document.getElementById('orl-budget-horizon').value); const summaryOutput = document.getElementById('orl-summary-output'); let html = ''; let grandTotalByYear = Array(budgetHorizonYears).fill(0); let grandTotalAllYears = 0; const properties = document.querySelectorAll('.orl-property-section'); if (properties.length === 0) { summaryOutput.innerHTML = '

No properties added. Please add properties in Tab 2.

'; downloadPdfButton.disabled = true; return; } properties.forEach(prop => { const propId = prop.dataset.propertyId; const propName = prop.querySelector('.orl-prop-name').value || `Property ${propId}`; const propArea = getNum(prop.querySelector('.orl-prop-area').value); const propAreaUnit = prop.querySelector('.orl-prop-area-unit').value || defaultAreaUnitInput.value || 'unit'; html += `

${propName} (Area: ${propArea} ${propAreaUnit})

`; html += ``; let propTotalAllYears = 0; // Collect one-time costs for this property let propertyOneTimeCostsTotal = 0; prop.querySelectorAll(`#orl-one-time-container-${propId} .orl-repeatable-item`).forEach(otcItem => { propertyOneTimeCostsTotal += getNum(otcItem.querySelector('.orl-otc-amount').value); }); for (let year = 1; year <= budgetHorizonYears; year++) { let annualBaseRent = calculateAnnualCost( getNum(prop.querySelector('.orl-br-amount').value), prop.querySelector('.orl-br-freq').value, prop.querySelector('.orl-br-esc-type').value, getNum(prop.querySelector('.orl-br-esc-val').value), getNum(prop.querySelector('.orl-br-esc-after').value), year, propArea, 'TotalPerPeriod' // Base rent is usually total ); let annualCam = calculateAnnualCost( getNum(prop.querySelector('.orl-cam-amount').value), prop.querySelector('.orl-cam-freq').value, prop.querySelector('.orl-cam-esc-type').value, getNum(prop.querySelector('.orl-cam-esc-val').value), getNum(prop.querySelector('.orl-cam-esc-after').value), year, propArea, prop.querySelector('.orl-cam-amount-type').value ); let annualOtherRecurring = 0; prop.querySelectorAll(`#orl-other-recurring-container-${propId} .orl-repeatable-item`).forEach(orcItem => { annualOtherRecurring += calculateAnnualCost( getNum(orcItem.querySelector('.orl-orc-amount').value), orcItem.querySelector('.orl-orc-freq').value, orcItem.querySelector('.orl-orc-esc-type').value, getNum(orcItem.querySelector('.orl-orc-esc-val').value), getNum(orcItem.querySelector('.orl-orc-esc-after').value), year, propArea, 'TotalPerPeriod' // Assuming other recurring are total amounts ); }); let yearOneTimeCosts = (year === 1) ? propertyOneTimeCostsTotal : 0; let annualTotalForPropertyYear = annualBaseRent + annualCam + annualOtherRecurring + yearOneTimeCosts; html += ``; propTotalAllYears += annualTotalForPropertyYear; grandTotalByYear[year-1] += annualTotalForPropertyYear; } html += ``; html += `
YearBase RentAdd. Rent/CAMOther RecurringOne-Time CostsAnnual Total
Year ${year} ${currency}${annualBaseRent.toFixed(2)} ${currency}${annualCam.toFixed(2)} ${currency}${annualOtherRecurring.toFixed(2)} ${currency}${yearOneTimeCosts.toFixed(2)} ${currency}${annualTotalForPropertyYear.toFixed(2)}
Total for ${propName} over ${budgetHorizonYears} Years:${currency}${propTotalAllYears.toFixed(2)}
`; grandTotalAllYears += propTotalAllYears; }); html += `

Overall Summary - All Properties

`; html += ``; for (let year = 0; year < budgetHorizonYears; year++) { html += ``; } html += ``; html += `
YearTotal Cost All Properties
Year ${year+1}${currency}${grandTotalByYear[year].toFixed(2)}
Grand Total Over ${budgetHorizonYears} Years:${currency}${grandTotalAllYears.toFixed(2)}
`; summaryOutput.innerHTML = html; downloadPdfButton.disabled = properties.length === 0; } // PDF Download downloadPdfButton.addEventListener('click', function () { if (typeof jspdf === 'undefined' || typeof jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library is not loaded.'); return; } const { jsPDF } = jspdf; const doc = new jsPDF('p'); // Portrait const currency = currencySymbolInput.value || '$'; const budgetHorizonYears = parseInt(document.getElementById('orl-budget-horizon').value); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--orl-primary-color').trim(); const secondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--orl-secondary-color').trim(); const textColor = getComputedStyle(document.documentElement).getPropertyValue('--orl-text-color').trim(); const buttonTextColor = getComputedStyle(document.documentElement).getPropertyValue('--orl-button-text-color').trim(); doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text('Office Rent & Lease Budget Plan', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); doc.setFontSize(12); doc.setTextColor(textColor); doc.text(`Business Name: ${document.getElementById('orl-business-name').value || 'N/A'}`, 14, 30); doc.text(`Budget Horizon: ${budgetHorizonYears} Years`, 14, 37); doc.text(`Currency: ${currency}`, 14, 44); doc.text(`Default Area Unit: ${defaultAreaUnitInput.value || 'unit'}`, 14, 51); let lastY = 60; let grandTotalAllYearsPdf = 0; const grandTotalByYearPdf = Array(budgetHorizonYears).fill(0); document.querySelectorAll('.orl-property-section').forEach(prop => { if (lastY > 250) { doc.addPage(); lastY = 20; } // Add new page if content gets too long const propId = prop.dataset.propertyId; const propName = prop.querySelector('.orl-prop-name').value || `Property ${propId}`; const propArea = getNum(prop.querySelector('.orl-prop-area').value); const propAreaUnit = prop.querySelector('.orl-prop-area-unit').value || defaultAreaUnitInput.value || 'unit'; doc.setFontSize(14); doc.setTextColor(primaryColor); doc.text(`${propName} (Area: ${propArea} ${propAreaUnit})`, 14, lastY); lastY += 7; const head = [['Year', 'Base Rent', 'Add. Rent/CAM', 'Other Recurring', 'One-Time Costs', 'Annual Total']]; const body = []; let propTotalAllYearsPdf = 0; let propertyOneTimeCostsTotal = 0; prop.querySelectorAll(`#orl-one-time-container-${propId} .orl-repeatable-item`).forEach(otcItem => { propertyOneTimeCostsTotal += getNum(otcItem.querySelector('.orl-otc-amount').value); }); // Add One-Time Costs summary for this property if (propertyOneTimeCostsTotal > 0) { const otcData = []; prop.querySelectorAll(`#orl-one-time-container-${propId} .orl-repeatable-item`).forEach(otcItem => { otcData.push([ otcItem.querySelector('.orl-otc-name').value || 'Unnamed Cost', `${currency}${getNum(otcItem.querySelector('.orl-otc-amount').value).toFixed(2)}` ]); }); if (otcData.length > 0) { doc.setFontSize(10); doc.setTextColor(textColor); doc.text('One-Time Costs:', 14, lastY); lastY += 5; doc.autoTable({ startY: lastY, theme: 'grid', head: [['Cost Name', 'Amount']], body: otcData, headStyles: { fillColor: secondaryColor, textColor: buttonTextColor, fontSize: 9 }, styles: { fontSize: 8, cellPadding: 1.5 } }); lastY = doc.lastAutoTable.finalY + 7; } } for (let year = 1; year <= budgetHorizonYears; year++) { let annualBaseRent = calculateAnnualCost( getNum(prop.querySelector('.orl-br-amount').value), prop.querySelector('.orl-br-freq').value, prop.querySelector('.orl-br-esc-type').value, getNum(prop.querySelector('.orl-br-esc-val').value), getNum(prop.querySelector('.orl-br-esc-after').value), year, propArea, 'TotalPerPeriod' ); let annualCam = calculateAnnualCost( getNum(prop.querySelector('.orl-cam-amount').value), prop.querySelector('.orl-cam-freq').value, prop.querySelector('.orl-cam-esc-type').value, getNum(prop.querySelector('.orl-cam-esc-val').value), getNum(prop.querySelector('.orl-cam-esc-after').value), year, propArea, prop.querySelector('.orl-cam-amount-type').value ); let annualOtherRecurring = 0; prop.querySelectorAll(`#orl-other-recurring-container-${propId} .orl-repeatable-item`).forEach(orcItem => { annualOtherRecurring += calculateAnnualCost( getNum(orcItem.querySelector('.orl-orc-amount').value), orcItem.querySelector('.orl-orc-freq').value, orcItem.querySelector('.orl-orc-esc-type').value, getNum(orcItem.querySelector('.orl-orc-esc-val').value), getNum(orcItem.querySelector('.orl-orc-esc-after').value), year, propArea, 'TotalPerPeriod' ); }); let yearOneTimeCosts = (year === 1) ? propertyOneTimeCostsTotal : 0; let annualTotalForPropertyYear = annualBaseRent + annualCam + annualOtherRecurring + yearOneTimeCosts; body.push([ `Year ${year}`, `${currency}${annualBaseRent.toFixed(2)}`, `${currency}${annualCam.toFixed(2)}`, `${currency}${annualOtherRecurring.toFixed(2)}`, `${currency}${yearOneTimeCosts.toFixed(2)}`, `${currency}${annualTotalForPropertyYear.toFixed(2)}` ]); propTotalAllYearsPdf += annualTotalForPropertyYear; grandTotalByYearPdf[year-1] += annualTotalForPropertyYear; } // Property Total Row body.push([ { content: `Total for ${propName}`, colSpan: 5, styles: { halign: 'right', fontStyle: 'bold' } }, { content: `${currency}${propTotalAllYearsPdf.toFixed(2)}`, styles: { halign: 'right', fontStyle: 'bold' } } ]); grandTotalAllYearsPdf += propTotalAllYearsPdf; doc.autoTable({ startY: lastY, head: head, body: body, theme: 'grid', headStyles: { fillColor: secondaryColor, textColor: buttonTextColor, fontSize: 9 }, styles: { fontSize: 8, cellPadding: 1.5 }, columnStyles: { // Align numbers to right in PDF table 1: { halign: 'right' }, 2: { halign: 'right' }, 3: { halign: 'right' }, 4: { halign: 'right' }, 5: { halign: 'right' } } }); lastY = doc.lastAutoTable.finalY + 10; }); if (lastY > 250) { doc.addPage(); lastY = 20; } doc.setFontSize(14); doc.setTextColor(primaryColor); doc.text('Overall Summary - All Properties', 14, lastY); lastY += 7; const summaryHead = [['Year', 'Total Cost All Properties']]; const summaryBody = []; for (let year = 0; year < budgetHorizonYears; year++) { summaryBody.push([`Year ${year+1}`, `${currency}${grandTotalByYearPdf[year].toFixed(2)}`]); } summaryBody.push([ { content: `Grand Total Over ${budgetHorizonYears} Years:`, styles: { fontStyle: 'bold', fillColor: primaryColor, textColor: buttonTextColor } }, { content: `${currency}${grandTotalAllYearsPdf.toFixed(2)}`, styles: { fontStyle: 'bold', halign: 'right', fillColor: primaryColor, textColor: buttonTextColor } } ]); doc.autoTable({ startY: lastY, head: summaryHead, body: summaryBody, theme: 'grid', headStyles: { fillColor: secondaryColor, textColor: buttonTextColor, fontSize: 10 }, styles: { fontSize: 9 }, columnStyles: { 1: { halign: 'right' } } }); doc.save(`Office_Lease_Budget_${document.getElementById('orl-business-name').value.replace(/\s+/g, '_') || 'Report'}.pdf`); }); checkEmptyState('#orl-properties-container', 'orl-no-items-text'); showTab(0); updateDynamicTextElements(); });
Scroll to Top