Emma – Duplex 5736

592500

Price

$ 592,500.00

Suggested Rent

$ 1850 per side

Taxes

1.74

%

HOA Fees

$ 990 per duplex annually

Leasing Fee

50.00

% of one month’s rent

Management fee

8.00

% of collected rent

116 Units Available
$ 592,500.00

Bedrooms

6

Bathrooms

4

Full

2

Half

Square footage

2,560

per unit

About:

2,560 sq ft total; 1,280 sq ft (per duplex side) includes 3 bedrooms, 2 1/2 baths. Central living, dining, kitchen, additional bar seating space, powder bath, large storage under stairs is all located downstairs. 3 bedrooms, 2 full baths, and laundry room are located upstairs. Each unit has 2 car garage, covered patio, large backyard, full irrigation, and privacy fence.

Key Features:

  • Luxury flooring
  • High ceilings
  • Stainless Steel Appliances
  • Large backyards

Schools:

School Name

Grades

Distance

Moe & Gene Johnson High School
9-12
8 miles
Dr T C Mccormick Jr Middle
6-8
1.7 miles
Ralph Pfluger Elementary School
PK-5
3.1 miles
Hays Consolidated Independent School District
PK-12
11 miles

Sitemap

Cashflow

Calculator

Estimate your potential returns with our interactive tool. Input purchase price, down payment, rental rates, and expenses — it will project your annual cash flow, cap rate, and ROI.

What our owners and investors say

“Owning a duplex with a VA loan changed everything. I’m building equity while reducing my housing costs.”

Owner Occupant

“The process was seamless—TwoTen kept us informed from start to finish. My duplex leased immediately.”

Investor Client

Duplex Gallery

Conventional Financing Calculator

INVESTMENT
Investment Capital Needed: $0.00
List Price
$
Down Payment
% $0.00
Closing Costs
$
Investment Capital Needed
$0.00
DEBT SERVICE
Total Mortgage Payment: $0.00
Loan Amount
70.0% $0.00
Interest Rate
%
Amortization
years
Mortgage Payment
$0.00
Additional Principal Payment
$
Total Mortgage Payment
$0.00
FIXED EXPENSES
Total Fixed Expenses: $0.00
Total Mortgage Payment
$0.00
Taxes
% $0.00
Insurance
$
Management Fee
% $0.00
Total Fixed Expenses
$0.00
VARIABLE EXPENSES
Total Variable Expenses: $0.00
Estimated Vacancy
% $0.00
Estimated Maintenance
% $0.00
Utilities
$
HOA Fee
$
Total Variable Expenses
$0.00
CASH FLOW
Estimated Cash Flow: $0.00
Gross Rent
$
Total Fixed Expenses
$0.00
Cash Flow With Fixed Expenses
$0.00
Total Variable Expenses
$0.00
Estimated Cash Flow
$0.00
RETURN ON INVESTMENT
Total Projected Profit: $0.00
Annual Appreciation
3%
Years Held
Accumulated Cash Flow
$0.00
Appreciation
$0.00
Principal Paydown
$0.00
Selling Expenses
$0.00
Total Projected Profit
$0.00
Annual Cash-on-Cash Return, Fixed Expenses
0.00%
Annual Cash-on-Cash Return, Total Expenses
0.00%
Annual Return on Investment
0.00%
Annual Cap Rate
0.00%
TAX BENEFITS
Annual Depreciation: $0.00
Annual Depreciation
$0.00
CASH RESERVES
Cash Reserves: $0.00
Number of Months
Cash Reserves
$0.00

Cash Purchase Calculator

Cash Purchase Investment Calculator
INVESTMENT
Investment Capital Needed: $0.00
List Price
$
Closing Costs
$
P = List Price
C% = Closing Costs Percent
Closing Costs = max(P × (C% ÷ 100), 1200)
C% varies by list price:
2% for $0 ≤ P ≤ $300,000
1.75% for $300,001 ≤ P ≤ $500,000
1.5% for $500,001 ≤ P ≤ $700,000
1.25% for $700,001 ≤ P ≤ $900,000
1% for $900,001 ≤ P ≤ $1,100,000
0.75% for $1,100,001 ≤ P ≤ $1,600,000
0.5% for P ≥ $1,600,001
Investment Capital Needed
$0.00
P = List Price
C = Closing Costs
Investment Capital Needed = P + C
FIXED EXPENSES
Total Fixed Expenses: $0.00
Taxes
$
Insurance
$
Management Fee
% $0.00
R = Gross Rent
V = Vacancy
M% = Management Fee Percent
Monthly Management Fee = (R - V) × M% ÷ 100
Total Fixed Expenses
$0.00
T = Taxes
I = Insurance
Mf = Management Fee
Total Fixed Expenses = T + I + Mf
VARIABLE EXPENSES
Total Variable Expenses: $0.00
Estimated Vacancy
% $0.00
R = Gross Rent
V% = Vacancy Percent
Estimated Vacancy = R × (V% ÷ 100)
Estimated Maintenance
% $0.00
R = Gross Rent
M% = Maintenance Percent
Estimated Maintenance = R × (M% ÷ 100)
Utilities
$
HOA Fee
$
Total Variable Expenses
$0.00
V = Estimated Vacancy
M = Estimated Maintenance
U = Utilities
H = HOA Fee
Total Variable Expenses = V + M + U + H
CASH FLOW
Estimated Cash Flow (total expenses): $0.00
Cash Flow (in year )
Gross Rent
Total Fixed Expenses
$0.00
Estimated Cash Flow (fixed expenses)
$0.00
R = Gross Rent
E_f = Total Fixed Expenses
Estimated Cash Flow (fixed expenses) = R - E_f
Total Variable Expenses
$0.00
Estimated Cash Flow (total expenses)
$0.00
R = Gross Rent
E_f = Total Fixed Expenses
E_v = Total Variable Expenses
Estimated Cash Flow (total expenses) = R - E_f - E_v
RETURN ON INVESTMENT
Total Projected Profit: $0.00
Annual Appreciation
0.0%
This number is based on average market trends.
Years Held
Accumulated Cash Flow
$0.00
C_NC = Monthly Cash Flow (total expenses) while property is new construction
C = Monthly Cash Flow (total expenses) while property is not new construction
M_NC = Months Held while the property is new construction
M = Months Held while property is not new construction
Accumulated Cash Flow = (C_NC * M_NC) + (C * M)
Appreciation
$0.00
P = List Price
A% = Annual Appreciation
Y = Years Held
Appreciation = P * (1 + A% / 100)^Y - P
Selling Expenses
$0.00
P = List Price
A = Appreciation
Selling Expenses = 0.075 * (P + A)
Total Projected Profit
$0.00
C = Accumulated Cash Flow
A = Appreciation
E = Selling Expenses
Total Projected Profit = C + A - E
Annual Cash on Cash Return (fixed expenses)
0.00%
CF_f(mo) = Monthly Cash Flow (fixed expenses)
I = Investment Capital
Annual Cash on Cash (fixed) = 100 · ((12 · CF_f(mo)) ÷ I)
Annual Cash on Cash Return (total expenses)
0.00%
CF_t(mo) = Monthly Cash Flow (total expenses)
I = Investment Capital
Annual Cash on Cash (total) = 100 · ((12 · CF_t(mo)) ÷ I)
Annual Return on Investment
0.00%
P = Total Projected Profit
I = Investment Capital
Y = Years Held
Annual Return on Investment = 100 · (P ÷ I ÷ Y)
Annual Cap Rate
0.00%
R = Annual Gross Rent
Ef = Annual Fixed Expenses
Ev = Annual Variable Expenses
P = List Price
Annual Cap Rate = (100 / P) * (R - Ef - Ev)
TAX BENEFITS
Annual Depreciation Value: $0.00
Annual Depreciation
$0.00
P = List Price
Annual Depreciation = P × 0.8 ÷ 27.5
CASH RESERVES
Cash Reserves: $0.00
Number of Months
Cash Reserves
$0.00
E_f = Total Fixed Expenses
E_v = Total Variable Expenses
M_f = Management Fee
V = Vacancy
n = Number of Months
Cash Reserves = max(n × (E_f + E_v - M_f - V), 3500)

Private Financing Calculator

Private Investment Calculator
SELECT LOAN PROGRAM
Selected Program: Program 2
Interest Rate / APR*
Min. Down Payment
Term / Amortization
6.95% / 7.41%
20%
30 Year / 30 Year
Selected
7.95% / 8.57%
30%
10 Year / 30 Year
8.95% / 9.47%
40%
30 Year / 30 Year
INVESTMENT
Investment Capital Needed: $0.00
List Price
$
Down Payment
% $154,800.00
P = List Price
D% = Down Payment Percent
Down Payment = P × (D% ÷ 100)
Closing Costs
$
P = Loan Amount
C% = Closing Costs Percent
Closing Costs = max(P × (C% ÷ 100), 1200)
C% varies by loan amount:
9.5% for $0 ≤ P ≤ $75,000
9% for $75,001 ≤ P ≤ $87,500
8.5% for $87,501 ≤ P ≤ $100,000
8% for $100,001 ≤ P ≤ $112,500
7.5% for $112,501 ≤ P ≤ $125,000
7% for $125,001 ≤ P ≤ $137,500
6.5% for $137,501 ≤ P ≤ $250,000
6% for $250,001 ≤ P ≤ $300,000
5.5% for $300,001 ≤ P ≤ $450,000
5% for $450,001 ≤ P ≤ $600,000
4.5% for $600,001 ≤ P ≤ $850,000
4% for $850,001 ≤ P ≤ $1,200,000
3.75% for $1,200,001 ≤ P ≤ $1,900,000
3.5% for $1,900,001 ≤ P ≤ $2,800,000
3.25% for $2,800,001 ≤ P ≤ $4,300,000
3% for P ≥ $4,300,001
Investment Capital Needed
$175,089.00
D = Down Payment
C = Closing Costs
Investment Capital Needed = D + C
DEBT SERVICE
Total Mortgage Payment: $0.00
Loan Amount
70 % $359,800.00
P = List Price
D% = Down Payment Percent
Loan Amount = P × (100 - D%) ÷ 100
Interest Rate
%
Amortization (years)
Term (years)
Mortgage Payment
($2,627.55)
P = Principal (Loan Amount)
i = Interest Rate ÷ 12 ÷ 100
n = Loan Amortization × 12
Monthly Mortgage Payment = P × (i × (1+i)^n) ÷ ((1+i)^n - 1)
Additional Principal Payment
$
Total Mortgage Payment
($2,627.55)
M = Mortgage Payment
A = Additional Principal Payment
Total Mortgage Payment = M + A
FIXED EXPENSES
Total Fixed Expenses: $0.00
Total Mortgage Payment
($2,627.55)
Taxes
% $0.00
Insurance
$
Management Fee
% ($165.19)
R = Gross Rent
V = Vacancy
M% = Management Fee Percent
Monthly Management Fee = (R - V) × M% ÷ 100
Total Fixed Expenses
($3,045.41)
M = Total Mortgage Payment
T = Taxes
I = Insurance
Mf = Management Fee
Total Fixed Expenses = M + T + I + Mf
VARIABLE EXPENSES
Total Variable Expenses: $0.00
Estimated Vacancy
% ($159.60)
R = Gross Rent
V% = Vacancy Percent
Estimated Vacancy = R × (V% ÷ 100)
Estimated Maintenance
% ($159.60)
R = Gross Rent
M% = Maintenance Percent
Estimated Maintenance = R × (M% ÷ 100)
Utilities
$
HOA Fee
$
Total Variable Expenses
($374.62)
V = Estimated Vacancy
M = Estimated Maintenance
U = Utilities
H = HOA Fee
Total Variable Expenses = V + M + U + H
CASH FLOW
Estimated Cash Flow (total expenses): $0.00
Cash Flow (in year )
Gross Rent
$
Total Fixed Expenses
$0.00
Estimated Cash Flow (fixed expenses)
$0.00
R = Gross Rent
Ef = Total Fixed Expenses
Estimated Cash Flow (fixed expenses) = R - Ef
Total Variable Expenses
$0.00
Estimated Cash Flow (total expenses)
$0.00
R = Gross Rent
Ef = Total Fixed Expenses
Ev = Total Variable Expenses
Estimated Cash Flow (total expenses) = R - Ef - Ev
RETURN ON INVESTMENT
Total Projected Profit: $0.00
Annual Appreciation
3 %
This number is based on average market trends.
Years Held
Accumulated Cash Flow
$0.00
C_NC = Monthly Cash Flow (total expenses) while property is new construction
C_M = Monthly Cash Flow (total expenses) while property is mortgaged
C_PO = Monthly Cash Flow (total expenses) while property is paid off
M_NC = Months Held while the property is new construction
M_M = Months Held while the property is mortgaged
M_PO = Months Held while the property is paid off
Accumulated Cash Flow = (C_NC · M_NC) + (C_M · M_M) + (C_PO · M_PO)
Appreciation
$0.00
P = List Price
A% = Annual Appreciation
Y = Years Held
Appreciation = P * (1 + A% / 100)^Y - P
Principal Paydown
$0.00
L = Loan Amount
i = Interest Rate ÷ 12 ÷ 100
n = Total Payments (Years Held × 12)
M = Monthly Payment
Principal Paydown = L - Remaining Balance after n payments
Selling Expenses
$0.00
P = List Price
A = Appreciation
Selling Expenses = 0.075 * (P + A)
Total Projected Profit
$0.00
C = Accumulated Cash Flow
A = Appreciation
PP = Principal Paydown
E = Selling Expenses
Total Projected Profit = C + A + PP - E
Annual Cash on Cash Return (fixed expenses)
0.00%
CF_f(mo) = Monthly Cash Flow (fixed expenses)
I = Investment Capital
Annual Cash on Cash (fixed) = 100 · ((12 · CF_f(mo)) ÷ I)
Annual Cash on Cash Return (total expenses)
0.00%
CF_t(mo) = Monthly Cash Flow (total expenses)
I = Investment Capital
Annual Cash on Cash (total) = 100 · ((12 · CF_t(mo)) ÷ I)
Annual Return on Investment
0.00%
P = Total Projected Profit
I = Investment Capital
Y = Years Held
Annual Return on Investment = 100 · (P ÷ I ÷ Y)
Annual Cap Rate
0.00%
R = Annual Gross Rent
Ef = Annual Fixed Expenses
Ev = Annual Variable Expenses
M = Annual Total Mortgage Payment
P = List Price
Annual Cap Rate = (100 / P) * (R - Ef - Ev + M)
TAX BENEFITS
Annual Depreciation Value: $0.00
Annual Depreciation
$0.00
P = List Price
Annual Depreciation = P × 0.8 ÷ 27.5
CASH RESERVES
Cash Reserves: $0.00
Number of Months
Cash Reserves
$0.00
E_f = Total Fixed Expenses
E_v = Total Variable Expenses
M_f = Management Fee
V = Vacancy
n = Number of Months
Cash Reserves = max(n × (E_f + E_v - M_f - V), 3500)
add_action('wp_footer', function () { ?> /* ============================================================================= * FILE 2 OF 2 — calculator.js (pair with index.html) * List price: floorplan sync respects data-user-edited / data-user-editing; property reset. * ============================================================================ */ function getCashPurchaseTabContent() { return document; } function formatCurrency(amount) { const isNegative = amount < 0; const absoluteAmount = Math.abs(amount); const formatted = '$' + absoluteAmount.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); return isNegative ? `-${formatted}` : formatted; } function isElementVisible(el) { if (!el) return false; let node = el; while (node && node.nodeType === 1) { const style = window.getComputedStyle(node); if (style.display === 'none' || style.visibility === 'hidden' || parseFloat(style.opacity) === 0) { return false; } node = node.parentElement; } return el.getClientRects().length > 0; } function repairMorganRidgeOptions(root) { const scope = root && root.querySelectorAll ? root : document; const dropdowns = scope.matches?.('.ir-cash-property-dropdown') ? [scope] : scope.querySelectorAll('.ir-cash-property-dropdown'); dropdowns.forEach((dropdown) => { dropdown.querySelectorAll('option').forEach((option) => { const label = option.textContent.trim(); let needsRepair = false; if (!option.value) { needsRepair = /Morgan['\u2019]s Ridge/i.test(label); } else { try { JSON.parse(option.value); } catch (e) { needsRepair = true; } } if (!needsRepair) return; const morganMatch = label.match(/^Morgan['\u2019]s Ridge\s*[–-]\s*Duplex\s*(\d+)$/i); if (!morganMatch) return; const prices = { '5736': 502000, '1299': 512000, '1484': 532000, '1676': 552000 }; const listPrice = prices[morganMatch[1]]; if (!listPrice) return; option.value = JSON.stringify({ name: "Morgan's Ridge – Duplex " + morganMatch[1], listPrice: listPrice, tax: 2.53, hoa: 800 }); }); }); } function applySelectedCashProperty(dropdown) { if (!dropdown || !dropdown.classList.contains('ir-cash-property-dropdown')) return; repairMorganRidgeOptions(dropdown); const selectedOption = dropdown.options[dropdown.selectedIndex]; let selectedValue = dropdown.value; if ((!selectedValue || selectedValue === '') && selectedOption) { selectedValue = selectedOption.value || ''; if (selectedValue) { dropdown.value = selectedValue; } } const propertyDetails = dropdown.closest('.ir-cash-property-selector')?.querySelector('.ir-cash-property-details'); if (!selectedValue) { if (propertyDetails) { propertyDetails.style.display = 'none'; } return; } try { const property = JSON.parse(selectedValue); document.querySelectorAll('.ir-cash-list-price-input').forEach((listPriceInput) => { if (!property.listPrice) return; listPriceInput.value = String(property.listPrice); listPriceInput.setAttribute('value', String(property.listPrice)); listPriceInput.setAttribute('data-user-edited', 'true'); listPriceInput.dispatchEvent(new Event('input', { bubbles: true })); }); if (property.tax) { const listPrice = property.listPrice || 0; const monthlyTaxes = (listPrice * property.tax) / 100 / 12; const activeFrequencyBtn = document.querySelector('.ir-cash-fixed-expenses-container .ir-cash-frequency-btn.ir-cash-active'); const frequency = activeFrequencyBtn?.dataset.frequency || 'monthly'; const calculatedTaxes = frequency === 'annually' ? (monthlyTaxes * 12) : monthlyTaxes; document.querySelectorAll('.ir-cash-taxes').forEach((taxesInput) => { taxesInput.value = calculatedTaxes.toFixed(2); taxesInput.setAttribute('data-user-edited', 'true'); taxesInput.dataset.userEdited = 'true'; taxesInput.dispatchEvent(new Event('input', { bubbles: true })); }); } if (property.hoa) { const monthlyHoa = property.hoa / 12; const activeFrequencyBtn = document.querySelector('.ir-cash-variable-expenses-container .ir-cash-frequency-btn.ir-cash-active'); const frequency = activeFrequencyBtn?.dataset.frequency || 'monthly'; const hoaValue = frequency === 'annually' ? property.hoa : monthlyHoa; document.querySelectorAll('.ir-cash-hoa-fee').forEach((hoaInput) => { hoaInput.value = hoaValue.toFixed(2); hoaInput.setAttribute('data-user-edited', 'true'); hoaInput.dataset.userEdited = 'true'; hoaInput.dispatchEvent(new Event('input', { bubbles: true })); }); } if (propertyDetails) { propertyDetails.style.display = 'block'; const nameEl = propertyDetails.querySelector('.ir-cash-property-detail-name'); const priceEl = propertyDetails.querySelector('.ir-cash-property-detail-price'); const taxEl = propertyDetails.querySelector('.ir-cash-property-detail-tax'); const hoaEl = propertyDetails.querySelector('.ir-cash-property-detail-hoa'); if (nameEl) nameEl.textContent = property.name || ''; if (priceEl) priceEl.textContent = formatCurrency(property.listPrice || 0); if (taxEl) taxEl.textContent = (property.tax || 0).toFixed(2) + '%'; if (hoaEl) hoaEl.textContent = formatCurrency(property.hoa || 0) + ' annually'; } if (window.CashPurchaseCalculator && window.CashPurchaseCalculator.calculate) { window.CashPurchaseCalculator.calculate(); } else if (typeof window.irCashCalculateAll === 'function') { window.irCashCalculateAll(); } } catch (e) { console.error('Error parsing property data:', e); } } window.toggleCashAccordion = function(sectionId) { const content = document.getElementById('cash-' + sectionId + '-content'); if (content) { const toggle = content.previousElementSibling.querySelector('.ir-cash-accordion-toggle'); if (content.classList.contains('ir-cash-expanded')) { content.classList.remove('ir-cash-expanded'); if (toggle) toggle.classList.remove('ir-cash-expanded'); } else { content.classList.add('ir-cash-expanded'); if (toggle) toggle.classList.add('ir-cash-expanded'); } } }; function initializeCashPurchaseCalculator() { const DEBUG_MODE = false; // Configuration for ROI behavior const cashRoiConfig = { newConstructionMonths: 0 }; function parseCurrency(currencyString) { if (!currencyString) return 0; const cleanAmount = currencyString.replace(/[^0-9.-]+/g, ''); return parseFloat(cleanAmount) || 0; } function getListPriceForCashPurchase() { const inputFields = document.querySelectorAll('.ir-cash-list-price-input'); let priceElement = document.querySelector('.floorplan-price'); if (!priceElement) { let searchContext = document; for (let i = 0; i < 5; i++) { if (!searchContext) break; priceElement = searchContext.querySelector('.floorplan-price'); if (priceElement) break; searchContext = searchContext.parentElement; } } if (priceElement) { const price = parseCurrency(priceElement.textContent); const safePrice = price >= 0 ? price : 0; inputFields.forEach((inputField) => { if (!isElementVisible(inputField)) return; const userEdited = inputField.getAttribute('data-user-edited') === 'true'; const userEditing = inputField.getAttribute('data-user-editing') === 'true'; if (!userEdited && !userEditing) { inputField.value = safePrice; inputField.setAttribute('value', String(safePrice)); } }); let returnPrice = safePrice; for (const inputField of inputFields) { if (!isElementVisible(inputField)) continue; const userEdited = inputField.getAttribute('data-user-edited') === 'true'; const userEditing = inputField.getAttribute('data-user-editing') === 'true'; if (userEdited || userEditing) { returnPrice = parseFloat(inputField.value) || 0; break; } } return returnPrice; } for (const inputField of inputFields) { if (!isElementVisible(inputField)) continue; const inputPrice = parseFloat(inputField.value) || 0; if (inputPrice > 0) return inputPrice; } if (inputFields.length > 0) { const inputPrice = parseFloat(inputFields[0].value) || 0; return inputPrice; } return 0; } function getTaxesFromContext(searchContext) { let taxesElement = searchContext.querySelector('.taxes'); if (!taxesElement) { if (searchContext !== document) { let parent = searchContext; for (let i = 0; i < 3; i++) { if (!parent) break; taxesElement = parent.querySelector('.taxes'); if (taxesElement) break; parent = parent.parentElement; } } if (!taxesElement) { taxesElement = document.querySelector('.taxes'); } } if (taxesElement) { const taxesPercentage = parseFloat(taxesElement.textContent.replace(/[^0-9.-]+/g, '')) || 0; const listPrice = getListPriceForCashPurchase(); const monthlyTaxes = (listPrice * taxesPercentage) / 100 / 12; const activeFrequencyBtn = document.querySelector('.ir-cash-fixed-expenses-container .ir-cash-frequency-btn.ir-cash-active'); const frequency = activeFrequencyBtn?.dataset.frequency || 'monthly'; const taxesAmount = frequency === 'annually' ? (monthlyTaxes * 12) : monthlyTaxes; const inputFields = document.querySelectorAll('.ir-cash-taxes'); inputFields.forEach(inputField => { if (inputField.dataset.userEdited !== 'true' && isElementVisible(inputField)) { inputField.value = taxesAmount.toFixed(2); } }); return taxesPercentage; } return 0; } function getManagementFromContext(searchContext) { let managementElement = searchContext.querySelector('.management'); if (!managementElement) { if (searchContext !== document) { let parent = searchContext; for (let i = 0; i < 3; i++) { if (!parent) break; managementElement = parent.querySelector('.management'); if (managementElement) break; parent = parent.parentElement; } } if (!managementElement) { managementElement = document.querySelector('.management'); } } if (managementElement) { const managementPercent = parseFloat(managementElement.textContent.replace(/[^0-9.-]+/g, '')) || 8; const inputFields = document.querySelectorAll('.ir-cash-management-percent'); inputFields.forEach(inputField => { if (inputField.dataset.userEdited !== 'true' && isElementVisible(inputField)) { inputField.value = managementPercent.toFixed(1); } }); return managementPercent; } return 8; } function getHoaFromContext(searchContext) { let hoaElement = searchContext.querySelector('.hoa'); if (!hoaElement) { if (searchContext !== document) { let parent = searchContext; for (let i = 0; i < 3; i++) { if (!parent) break; hoaElement = parent.querySelector('.hoa'); if (hoaElement) break; parent = parent.parentElement; } } if (!hoaElement) { hoaElement = document.querySelector('.hoa'); } } if (hoaElement) { const annualHoa = parseCurrency(hoaElement.textContent); const monthlyHoa = annualHoa / 12; const activeFrequencyBtn = document.querySelector('.ir-cash-variable-expenses-container .ir-cash-frequency-btn.ir-cash-active'); const frequency = activeFrequencyBtn?.dataset.frequency || 'monthly'; const hoaValue = frequency === 'annually' ? (monthlyHoa * 12) : monthlyHoa; const inputFields = document.querySelectorAll('.ir-cash-hoa-fee'); inputFields.forEach(inputField => { if (inputField.dataset.userEdited !== 'true' && isElementVisible(inputField)) { inputField.value = hoaValue.toFixed(2); } }); return hoaValue; } return 0; } function getLeasingFromContext(searchContext) { let leasingElement = searchContext.querySelector('.leasing'); if (!leasingElement) { if (searchContext !== document) { let parent = searchContext; for (let i = 0; i < 3; i++) { if (!parent) break; leasingElement = parent.querySelector('.leasing'); if (leasingElement) break; parent = parent.parentElement; } } if (!leasingElement) { leasingElement = document.querySelector('.leasing'); } } if (leasingElement) { const leasingPercent = parseFloat(leasingElement.textContent.replace(/[^0-9.-]+/g, '')) || 50; const inputFields = document.querySelectorAll('.ir-cash-leasing-fee-percent'); inputFields.forEach(inputField => { if (inputField.dataset.userEdited !== 'true' && isElementVisible(inputField)) { inputField.value = leasingPercent.toFixed(1); } }); return leasingPercent; } return 50; } const cashPurchaseCalc = { closingCostsTable: [ { min: 0, max: 300000, percent: 2 }, { min: 300001, max: 500000, percent: 1.75 }, { min: 500001, max: 700000, percent: 1.5 }, { min: 700001, max: 900000, percent: 1.25 }, { min: 900001, max: 1100000, percent: 1 }, { min: 1100001, max: 1600000, percent: 0.75 }, { min: 1600001, max: Infinity, percent: 0.5 } ], getClosingCostsPercent: function(listPrice) { for (let tier of this.closingCostsTable) { if (listPrice >= tier.min && listPrice <= tier.max) { return tier.percent; } } return 0.5; }, calculate: function() { const listPrice = getListPriceForCashPurchase(); const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; const closingCostsInput = tabContent.querySelector('.ir-cash-closing-costs-input'); let closingCosts = parseFloat(closingCostsInput?.value) || 0; // Recalculate closing costs if it's 0 or if user hasn't manually edited it if (closingCosts === 0 || closingCostsInput?.dataset.userEdited !== 'true') { const closingCostsPercent = this.getClosingCostsPercent(listPrice); closingCosts = Math.max(listPrice * (closingCostsPercent / 100), 1200); // Update the input with calculated value if (closingCostsInput) { closingCostsInput.value = closingCosts.toFixed(0); } } const totalInvestment = listPrice + closingCosts; const totalAmountEls = tabContent.querySelectorAll('.ir-cash-total-amount'); totalAmountEls.forEach(function(el) { el.textContent = formatCurrency(totalInvestment); }); } }; const cashFixedExpensesCalc = { calculate: function() { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; const taxesInput = tabContent.querySelector('.ir-cash-taxes'); const insuranceInput = tabContent.querySelector('.ir-cash-insurance'); const managementPercentInput = tabContent.querySelector('.ir-cash-management-percent'); const activeFrequencyBtn = tabContent.querySelector('.ir-cash-fixed-expenses-container .ir-cash-frequency-btn.ir-cash-active'); const frequency = activeFrequencyBtn?.dataset.frequency || 'monthly'; const taxes = parseFloat(taxesInput?.value) || 0; const insurance = parseFloat(insuranceInput?.value) || 0; const managementPercent = parseFloat(managementPercentInput?.value) || 8; // Get gross rent from cash flow container and normalize to monthly const grossRentInput = tabContent.querySelector('.ir-cash-gross-rent'); let grossRent = parseFloat(grossRentInput?.value) || 2500; // Check if cash flow section is in annual mode - if so, convert gross rent to monthly const cashFlowContainer = tabContent.querySelector('.ir-cash-cash-flow-container'); const cashFlowContent = cashFlowContainer?.querySelector('#cash-cash-flow-content'); const cashFlowActiveBtn = cashFlowContent?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const cashFlowFrequency = cashFlowActiveBtn?.dataset.frequency || 'monthly'; if (cashFlowFrequency === 'annually') { grossRent = grossRent / 12; } // Also check if gross rent seems too large (likely already annual) if (grossRent > 10000) { grossRent = grossRent / 12; } const vacancyPercentInput = tabContent.querySelector('.ir-cash-vacancy-percent'); const vacancyPercent = parseFloat(vacancyPercentInput?.value) || 5; const vacancy = grossRent * (vacancyPercent / 100); // Management fee calculation (always based on monthly gross rent) const monthlyManagementFee = (grossRent - vacancy) * (managementPercent / 100); let displayManagementFee = monthlyManagementFee; if (frequency === 'annually') { displayManagementFee = monthlyManagementFee * 12; } // Total fixed expenses (input values are already in the correct frequency) let totalFixedExpenses = taxes + insurance + displayManagementFee; const managementFeeEl = tabContent.querySelector('.ir-cash-management-fee'); const headerTotalFixedExpensesEl = tabContent.querySelector('.ir-cash-fixed-expenses-header .ir-cash-total-fixed-expenses'); const bottomTotalFixedExpensesEl = tabContent.querySelector('.ir-cash-fixed-expenses-total-item .ir-cash-total-fixed-expenses'); if (managementFeeEl) managementFeeEl.textContent = formatCurrency(displayManagementFee); if (headerTotalFixedExpensesEl) headerTotalFixedExpensesEl.textContent = formatCurrency(totalFixedExpenses); if (bottomTotalFixedExpensesEl) bottomTotalFixedExpensesEl.textContent = formatCurrency(totalFixedExpenses); } }; const cashVariableExpensesCalc = { calculate: function() { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; const vacancyPercentInput = tabContent.querySelector('.ir-cash-vacancy-percent'); const maintenancePercentInput = tabContent.querySelector('.ir-cash-maintenance-percent'); const utilitiesInput = tabContent.querySelector('.ir-cash-utilities'); const hoaFeeInput = tabContent.querySelector('.ir-cash-hoa-fee'); const vacancyPercent = parseFloat(vacancyPercentInput?.value) || 5; const maintenancePercent = parseFloat(maintenancePercentInput?.value) || 3; const utilities = parseFloat(utilitiesInput?.value) || 0; const hoaFee = parseFloat(hoaFeeInput?.value) || 0; // Get gross rent from cash flow container and normalize to monthly const grossRentInput = tabContent.querySelector('.ir-cash-gross-rent'); let grossRent = parseFloat(grossRentInput?.value) || 2500; // Check if cash flow section is in annual mode - if so, convert gross rent to monthly const cashFlowContainer = tabContent.querySelector('.ir-cash-cash-flow-container'); const cashFlowContent = cashFlowContainer?.querySelector('#cash-cash-flow-content'); const cashFlowActiveBtn = cashFlowContent?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const cashFlowFrequency = cashFlowActiveBtn?.dataset.frequency || 'monthly'; if (cashFlowFrequency === 'annually') { grossRent = grossRent / 12; } // Also check if gross rent seems too large (likely already annual) if (grossRent > 10000) { grossRent = grossRent / 12; } const monthlyVacancy = grossRent * (vacancyPercent / 100); const monthlyMaintenance = grossRent * (maintenancePercent / 100); // Respect variable expenses frequency const frequencyBtn = tabContent.querySelector('.ir-cash-variable-expenses-container .ir-cash-frequency-btn.ir-cash-active'); const frequency = frequencyBtn?.dataset.frequency || 'monthly'; const estimatedVacancy = frequency === 'annually' ? (monthlyVacancy * 12) : monthlyVacancy; const estimatedMaintenance = frequency === 'annually' ? (monthlyMaintenance * 12) : monthlyMaintenance; // Utilities and HOA are already in the correct frequency (read from input) const totalVariableExpenses = estimatedVacancy + estimatedMaintenance + utilities + hoaFee; const estimatedVacancyEl = tabContent.querySelector('.ir-cash-estimated-vacancy'); const estimatedMaintenanceEl = tabContent.querySelector('.ir-cash-estimated-maintenance'); const headerTotalVariableExpensesEl = tabContent.querySelector('.ir-cash-variable-expenses-header .ir-cash-total-variable-expenses'); const bottomTotalVariableExpensesEl = tabContent.querySelector('.ir-cash-variable-expenses-total-item .ir-cash-total-variable-expenses'); if (estimatedVacancyEl) estimatedVacancyEl.textContent = formatCurrency(estimatedVacancy); if (estimatedMaintenanceEl) estimatedMaintenanceEl.textContent = formatCurrency(estimatedMaintenance); if (headerTotalVariableExpensesEl) headerTotalVariableExpensesEl.textContent = formatCurrency(totalVariableExpenses); if (bottomTotalVariableExpensesEl) bottomTotalVariableExpensesEl.textContent = formatCurrency(totalVariableExpenses); } }; const cashFlowCalc = { calculate: function() { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; const grossRentInput = tabContent.querySelector('.ir-cash-gross-rent'); let grossRent = parseFloat(grossRentInput?.value) || 2500; // Get cash flow container frequency const cashFlowContainer = tabContent.querySelector('.ir-cash-cash-flow-container'); const cashFlowContent = cashFlowContainer?.querySelector('#cash-cash-flow-content'); const activeFrequencyBtn = cashFlowContent?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const cashFlowFrequency = activeFrequencyBtn?.dataset.frequency || 'monthly'; // Read calculated values from other calculators within the same tab const totalFixedExpensesEl = tabContent.querySelector('.ir-cash-fixed-expenses-total-item .ir-cash-total-fixed-expenses'); const totalVariableExpensesEl = tabContent.querySelector('.ir-cash-variable-expenses-total-item .ir-cash-total-variable-expenses'); let totalFixedExpenses = parseCurrency(totalFixedExpensesEl?.textContent) || 0; let totalVariableExpenses = parseCurrency(totalVariableExpensesEl?.textContent) || 0; // Normalize to monthly first, then convert to cash flow frequency const fixedExpensesContainer = tabContent.querySelector('.ir-cash-fixed-expenses-container'); const variableExpensesContainer = tabContent.querySelector('.ir-cash-variable-expenses-container'); const fixedActiveBtn = fixedExpensesContainer?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const variableActiveBtn = variableExpensesContainer?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const fixedFrequency = fixedActiveBtn?.dataset.frequency || 'monthly'; const variableFrequency = variableActiveBtn?.dataset.frequency || 'monthly'; // Normalize gross rent to monthly first (input value is already in the correct frequency based on toggle) if (cashFlowFrequency === 'annually') { grossRent = grossRent / 12; } // Also check for heuristic (large values might be annual) if (grossRent > 10000) { grossRent = grossRent / 12; } // Convert expenses to monthly if (fixedFrequency === 'annually') { totalFixedExpenses = totalFixedExpenses / 12; } if (variableFrequency === 'annually') { totalVariableExpenses = totalVariableExpenses / 12; } // Convert to cash flow frequency for display if (cashFlowFrequency === 'annually') { grossRent = grossRent * 12; totalFixedExpenses = totalFixedExpenses * 12; totalVariableExpenses = totalVariableExpenses * 12; } const estimatedCashFlowFixed = grossRent - totalFixedExpenses; const estimatedCashFlowTotal = grossRent - totalFixedExpenses - totalVariableExpenses; // Update header summary const headerCashFlowTotalEl = tabContent.querySelector('.ir-cash-cash-flow-header .ir-cash-estimated-cash-flow-total'); if (headerCashFlowTotalEl) headerCashFlowTotalEl.textContent = formatCurrency(estimatedCashFlowTotal); // Update content displays const grossRentDisplayEl = tabContent.querySelector('.ir-cash-gross-rent-display'); const totalFixedExpensesDisplayEl = tabContent.querySelector('.ir-cash-total-fixed-expenses-display'); const estimatedCashFlowFixedEl = tabContent.querySelector('.ir-cash-estimated-cash-flow-fixed'); const totalVariableExpensesDisplayEl = tabContent.querySelector('.ir-cash-total-variable-expenses-display'); const estimatedCashFlowTotalEl = tabContent.querySelector('.ir-cash-estimated-cash-flow-total'); const cashFlowContentNode = tabContent.querySelector('#cash-cash-flow-content'); let estimatedCashFlowTotalContentEl = null; if (cashFlowContentNode) { const allTotals = cashFlowContentNode.querySelectorAll('.ir-cash-estimated-cash-flow-total'); if (allTotals && allTotals.length) { estimatedCashFlowTotalContentEl = allTotals[allTotals.length - 1]; } } if (grossRentDisplayEl) grossRentDisplayEl.textContent = formatCurrency(grossRent); if (totalFixedExpensesDisplayEl) totalFixedExpensesDisplayEl.textContent = formatCurrency(totalFixedExpenses); if (estimatedCashFlowFixedEl) estimatedCashFlowFixedEl.textContent = formatCurrency(estimatedCashFlowFixed); if (totalVariableExpensesDisplayEl) totalVariableExpensesDisplayEl.textContent = formatCurrency(totalVariableExpenses); if (estimatedCashFlowTotalEl) estimatedCashFlowTotalEl.textContent = formatCurrency(estimatedCashFlowTotal); if (estimatedCashFlowTotalContentEl) estimatedCashFlowTotalContentEl.textContent = formatCurrency(estimatedCashFlowTotal); } }; const roiCalc = { calculate: function() { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; // Get inputs const yearsHeldInput = tabContent.querySelector('.ir-cash-years-held'); const yearsHeld = parseFloat(yearsHeldInput?.value) || 0; const monthsNewConstruction = cashRoiConfig.newConstructionMonths || 0; // Get list price const listPriceInput = tabContent.querySelector('.ir-cash-list-price-input'); const listPrice = parseFloat(listPriceInput?.value) || 0; // Calculate monthly cash flow directly from inputs (always monthly, regardless of toggles) const cashFlowContainer = tabContent.querySelector('.ir-cash-cash-flow-container'); const cashFlowContent = cashFlowContainer?.querySelector('#cash-cash-flow-content'); const cashFlowActiveBtn = cashFlowContent?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const cashFlowFrequency = cashFlowActiveBtn?.dataset.frequency || 'monthly'; const grossRentInput = tabContent.querySelector('.ir-cash-gross-rent'); let grossRent = parseFloat(grossRentInput?.value) || 2500; if (cashFlowFrequency === 'annually' || grossRent > 10000) { grossRent = grossRent / 12; } const fixedExpensesContainer = tabContent.querySelector('.ir-cash-fixed-expenses-container'); const variableExpensesContainer = tabContent.querySelector('.ir-cash-variable-expenses-container'); const fixedActiveBtn = fixedExpensesContainer?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const variableActiveBtn = variableExpensesContainer?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const fixedFrequency = fixedActiveBtn?.dataset.frequency || 'monthly'; const variableFrequency = variableActiveBtn?.dataset.frequency || 'monthly'; // Calculate monthly fixed expenses from inputs const taxesInput = tabContent.querySelector('.ir-cash-taxes'); const insuranceInput = tabContent.querySelector('.ir-cash-insurance'); const managementPercentInput = tabContent.querySelector('.ir-cash-management-percent'); let monthlyTaxes = parseFloat(taxesInput?.value) || 0; let monthlyInsurance = parseFloat(insuranceInput?.value) || 125; if (fixedFrequency === 'annually') { monthlyTaxes = monthlyTaxes / 12; monthlyInsurance = monthlyInsurance / 12; } const managementPercent = parseFloat(managementPercentInput?.value) || 8; const vacancyPercentInput = tabContent.querySelector('.ir-cash-vacancy-percent'); const vacancyPercent = parseFloat(vacancyPercentInput?.value) || 5; const monthlyVacancy = grossRent * (vacancyPercent / 100); const netRent = grossRent - monthlyVacancy; const monthlyManagementFee = netRent * (managementPercent / 100); const monthlyFixedExpenses = monthlyTaxes + monthlyInsurance + monthlyManagementFee; // Calculate monthly variable expenses from inputs const maintenancePercentInput = tabContent.querySelector('.ir-cash-maintenance-percent'); const utilitiesInput = tabContent.querySelector('.ir-cash-utilities'); const hoaFeeInput = tabContent.querySelector('.ir-cash-hoa-fee'); const maintenancePercent = parseFloat(maintenancePercentInput?.value) || 3; const monthlyMaintenance = grossRent * (maintenancePercent / 100); let monthlyUtilities = parseFloat(utilitiesInput?.value) || 0; let monthlyHoaFee = parseFloat(hoaFeeInput?.value) || 0; if (variableFrequency === 'annually') { monthlyUtilities = monthlyUtilities / 12; monthlyHoaFee = monthlyHoaFee / 12; } const monthlyVariableExpenses = monthlyVacancy + monthlyMaintenance + monthlyUtilities + monthlyHoaFee; // Calculate monthly cash flows const monthlyCashFlowFixed = grossRent - monthlyFixedExpenses; const monthlyCashFlowTotal = grossRent - monthlyFixedExpenses - monthlyVariableExpenses; // Annual Appreciation calculation - fixed at 3% const annualAppreciation = 3.0; // Appreciation calculation const appreciation = listPrice * Math.pow(1 + annualAppreciation / 100, yearsHeld) - listPrice; // Selling Expenses (7.5% of List Price + Appreciation) const sellingExpenses = 0.075 * (listPrice + appreciation); // Accumulated Cash Flow (always using monthly values) const totalMonths = yearsHeld * 12; const accumulatedCashFlow = monthlyCashFlowTotal * totalMonths; // Total Projected Profit = Cash Flow + Appreciation - Selling Expenses const totalProjectedProfit = accumulatedCashFlow + appreciation - sellingExpenses; // Annual Cash on Cash Return calculations // Get investment capital from investment container const totalInvestmentEl = tabContent.querySelector('.ir-cash-total-amount'); const investmentCapital = parseCurrency(totalInvestmentEl?.textContent) || listPrice; // Annual Cash on Cash Return (Fixed) = (Monthly Cash Flow Fixed × 12) / Investment Capital × 100 const annualCashOnCashFixed = investmentCapital > 0 ? (monthlyCashFlowFixed * 12 / investmentCapital) * 100 : 0; // Annual Cash on Cash Return (Total) = (Monthly Cash Flow Total × 12) / Investment Capital × 100 const annualCashOnCashTotal = investmentCapital > 0 ? (monthlyCashFlowTotal * 12 / investmentCapital) * 100 : 0; // Annual Return on Investment const annualROI = yearsHeld > 0 ? (100 * totalProjectedProfit / investmentCapital / yearsHeld) : 0; // Annual Cap Rate - always using monthly values const annualGrossRent = grossRent * 12; const annualFixedExpenses = monthlyFixedExpenses * 12; const annualVariableExpenses = monthlyVariableExpenses * 12; const annualCapRate = listPrice > 0 ? (100 / listPrice) * (annualGrossRent - annualFixedExpenses - annualVariableExpenses) : 0; // Update display elements const annualAppreciationEl = tabContent.querySelector('.ir-cash-annual-appreciation'); const appreciationEl = tabContent.querySelector('.ir-cash-appreciation'); const sellingExpensesEl = tabContent.querySelector('.ir-cash-selling-expenses'); const accumulatedCashFlowEl = tabContent.querySelector('.ir-cash-accumulated-cash-flow'); const annualCashOnCashFixedEl = tabContent.querySelector('.ir-cash-annual-cash-on-cash-fixed'); const annualCashOnCashTotalEl = tabContent.querySelector('.ir-cash-annual-cash-on-cash-total'); const annualROIEl = tabContent.querySelector('.ir-cash-annual-roi'); const annualCapRateEl = tabContent.querySelector('.ir-cash-annual-cap-rate'); if (annualAppreciationEl) annualAppreciationEl.textContent = '3.0%'; if (appreciationEl) appreciationEl.textContent = formatCurrency(appreciation); if (sellingExpensesEl) sellingExpensesEl.textContent = formatCurrency(sellingExpenses); if (accumulatedCashFlowEl) accumulatedCashFlowEl.textContent = formatCurrency(accumulatedCashFlow); if (annualCashOnCashFixedEl) annualCashOnCashFixedEl.textContent = annualCashOnCashFixed.toFixed(2) + '%'; if (annualCashOnCashTotalEl) annualCashOnCashTotalEl.textContent = annualCashOnCashTotal.toFixed(2) + '%'; if (annualROIEl) annualROIEl.textContent = annualROI.toFixed(2) + '%'; if (annualCapRateEl) annualCapRateEl.textContent = annualCapRate.toFixed(2) + '%'; // Update all Total Projected Profit elements (both header and content) const totalProjectedProfitEls = tabContent.querySelectorAll('.ir-cash-total-projected-profit'); totalProjectedProfitEls.forEach(function(el) { el.textContent = formatCurrency(totalProjectedProfit); }); } }; const taxBenefitsCalc = { calculate: function() { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; // Get list price from investment calculator const listPriceInput = tabContent.querySelector('.ir-cash-list-price-input'); const listPrice = parseFloat(listPriceInput?.value) || 0; // Calculate Annual Depreciation: P × 0.8 ÷ 27.5 const annualDepreciation = listPrice * 0.8 / 27.5; // Update display elements const annualDepreciationValueEls = tabContent.querySelectorAll('.ir-cash-annual-depreciation-value'); annualDepreciationValueEls.forEach(function(el) { el.textContent = formatCurrency(annualDepreciation); }); } }; const cashReservesCalc = { calculate: function() { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; // Get number of months input const monthsReservesInput = tabContent.querySelector('.ir-cash-months-reserves'); const numberOfMonths = parseFloat(monthsReservesInput?.value) || 6; // Get values from other calculators and always normalize to monthly const fixedExpensesContainer = tabContent.querySelector('.ir-cash-fixed-expenses-container'); const variableExpensesContainer = tabContent.querySelector('.ir-cash-variable-expenses-container'); const fixedActiveBtn = fixedExpensesContainer?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const variableActiveBtn = variableExpensesContainer?.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const fixedFrequency = fixedActiveBtn?.dataset.frequency || 'monthly'; const variableFrequency = variableActiveBtn?.dataset.frequency || 'monthly'; const totalFixedExpensesEl = tabContent.querySelector('.ir-cash-fixed-expenses-total-item .ir-cash-total-fixed-expenses'); const totalVariableExpensesEl = tabContent.querySelector('.ir-cash-variable-expenses-total-item .ir-cash-total-variable-expenses'); const managementFeeEl = tabContent.querySelector('.ir-cash-management-fee'); const estimatedVacancyEl = tabContent.querySelector('.ir-cash-estimated-vacancy'); let monthlyFixedExpenses = parseCurrency(totalFixedExpensesEl?.textContent) || 0; let monthlyVariableExpenses = parseCurrency(totalVariableExpensesEl?.textContent) || 0; let monthlyManagementFee = parseCurrency(managementFeeEl?.textContent) || 0; let monthlyVacancy = parseCurrency(estimatedVacancyEl?.textContent) || 0; // Always normalize to monthly regardless of toggle if (fixedFrequency === 'annually') { monthlyFixedExpenses = monthlyFixedExpenses / 12; monthlyManagementFee = monthlyManagementFee / 12; } if (variableFrequency === 'annually') { monthlyVariableExpenses = monthlyVariableExpenses / 12; monthlyVacancy = monthlyVacancy / 12; } // Calculate Cash Reserves: max(n × (E_f + E_v - M_f - V), 3500) // Always using monthly values const calculation = numberOfMonths * (monthlyFixedExpenses + monthlyVariableExpenses - monthlyManagementFee - monthlyVacancy); const cashReserves = Math.max(calculation, 3500); // Update display elements const cashReservesValueEls = tabContent.querySelectorAll('.ir-cash-cash-reserves-value'); cashReservesValueEls.forEach(function(el) { el.textContent = formatCurrency(cashReserves); }); } }; function calculateCashPurchase() { cashPurchaseCalc.calculate(); cashFixedExpensesCalc.calculate(); cashVariableExpensesCalc.calculate(); cashFlowCalc.calculate(); roiCalc.calculate(); taxBenefitsCalc.calculate(); cashReservesCalc.calculate(); } function toggleComputation(computationId) { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; const computation = tabContent.querySelector('#' + computationId); if (computation) { computation.classList.toggle('ir-cash-show'); } } // Attach functions to window for event listeners window.CashPurchaseCalculator = { calculate: calculateCashPurchase, toggleComputation: toggleComputation }; window.irCashCalculateAll = calculateCashPurchase; const listPrice = getListPriceForCashPurchase(); const taxes = getTaxesFromContext(document); const management = getManagementFromContext(document); const hoa = getHoaFromContext(document); const insuranceInput = document.querySelector('.ir-cash-insurance'); if (insuranceInput && (parseFloat(insuranceInput.value) === 0 || !insuranceInput.value)) { const activeFrequencyBtn = document.querySelector('.ir-cash-fixed-expenses-container .ir-cash-frequency-btn.ir-cash-active'); const frequency = activeFrequencyBtn?.dataset.frequency || 'monthly'; const insuranceValue = frequency === 'annually' ? 1500 : 125; insuranceInput.value = insuranceValue.toFixed(2); } calculateCashPurchase(); const yearInput = document.querySelector('.ir-cash-year-input'); const yearsHeldInput = document.querySelector('.ir-cash-years-held'); if (yearInput && yearsHeldInput) { yearsHeldInput.value = yearInput.value; } repairMorganRidgeOptions(document); } document.addEventListener('DOMContentLoaded', function() { initializeCashPurchaseCalculator(); }); if (document.readyState === 'complete' || document.readyState === 'interactive') { initializeCashPurchaseCalculator(); } const cashPopupObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { const calculatorContainer = node.querySelector?.('.ir-cash-investment-container'); if (calculatorContainer) { setTimeout(() => { initializeCashPurchaseCalculator(); }, 100); } } }); }); }); cashPopupObserver.observe(document.body, { childList: true, subtree: true }); // Removed conflicting earlier fx handler; unified handler below manages toggles // Initialize if (document.readyState === 'loading') { // Will be initialized by DOMContentLoaded listener } else { initializeCashPurchaseCalculator(); } // Event listeners - only for cash purchase calculator document.addEventListener('input', function(e) { if (e.target.matches('.ir-cash-list-price-input, .ir-cash-closing-costs-input, .ir-cash-taxes, .ir-cash-insurance, .ir-cash-management-percent, .ir-cash-hoa-fee, .ir-cash-utilities, .ir-cash-vacancy-percent, .ir-cash-maintenance-percent, .ir-cash-gross-rent, .ir-cash-years-held, .ir-cash-year-input, .ir-cash-months-new-construction, .ir-cash-months-reserves')) { // Mark user edits so auto-populators don't overwrite them if (e.target.matches('.ir-cash-list-price-input')) { e.target.setAttribute('data-user-edited', 'true'); } if (e.target.matches('.ir-cash-taxes, .ir-cash-management-percent, .ir-cash-closing-costs-input')) { e.target.dataset.userEdited = 'true'; } if (window.CashPurchaseCalculator && window.CashPurchaseCalculator.calculate) { window.CashPurchaseCalculator.calculate(); } } // Two-way sync between cash flow year input and ROI years held if (e.target.matches('.ir-cash-year-input')) { const tabContent = getCashPurchaseTabContent(); if (tabContent) { const yearsHeldInput = tabContent.querySelector('.ir-cash-years-held'); if (yearsHeldInput) { yearsHeldInput.value = e.target.value; } } } if (e.target.matches('.ir-cash-years-held')) { const tabContent = getCashPurchaseTabContent(); if (tabContent) { const yearInput = tabContent.querySelector('.ir-cash-year-input'); if (yearInput) { yearInput.value = e.target.value; } } } }); document.addEventListener('click', function(e) { const prTarget = e.target.closest('.ir-cash-pr-rating'); if (prTarget) { const tabContent = getCashPurchaseTabContent(); if (tabContent) { tabContent.querySelectorAll('.ir-cash-pr-rating').forEach(function(el){ el.classList.remove('ir-cash-active-pr'); }); prTarget.classList.add('ir-cash-active-pr'); if (window.CashPurchaseCalculator && window.CashPurchaseCalculator.calculate) { window.CashPurchaseCalculator.calculate(); } } } const button = e.target.closest('.ir-cash-formula-btn'); if (button) { e.stopPropagation(); const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; const container = button.closest('.ir-cash-investment-container, .ir-cash-fixed-expenses-container, .ir-cash-variable-expenses-container, .ir-cash-cash-flow-container, .ir-cash-roi-container, .ir-cash-tax-benefits-container, .ir-cash-cash-reserves-container'); if (!container || !tabContent.contains(container)) return; if (button.textContent.trim() === 'fx') { // Investment section buttons if (button.closest('.ir-cash-investment-item') && button.closest('.ir-cash-investment-item').querySelector('.ir-cash-closing-costs-input')) { window.CashPurchaseCalculator.toggleComputation('cash-closing-costs-computation'); } else if (button.closest('.ir-cash-investment-total-item')) { window.CashPurchaseCalculator.toggleComputation('cash-investment-capital-computation'); } // Fixed expenses section buttons else if (button.closest('.ir-cash-fixed-expenses-item') && button.closest('.ir-cash-fixed-expenses-item').querySelector('.ir-cash-management-fee')) { window.CashPurchaseCalculator.toggleComputation('cash-management-fee-computation'); } else if (button.closest('.ir-cash-fixed-expenses-total-item')) { window.CashPurchaseCalculator.toggleComputation('cash-total-fixed-expenses-computation'); } // Variable expenses section buttons else if (button.closest('.ir-cash-variable-expenses-item')) { const item = button.closest('.ir-cash-variable-expenses-item'); if (item.querySelector('.ir-cash-estimated-vacancy')) { window.CashPurchaseCalculator.toggleComputation('cash-estimated-vacancy-computation'); } else if (item.querySelector('.ir-cash-estimated-maintenance')) { window.CashPurchaseCalculator.toggleComputation('cash-estimated-maintenance-computation'); } } else if (button.closest('.ir-cash-variable-expenses-total-item')) { window.CashPurchaseCalculator.toggleComputation('cash-total-variable-expenses-computation'); } // Cash flow section buttons else if (button.closest('.ir-cash-cash-flow-item') && button.closest('.ir-cash-cash-flow-item').querySelector('.ir-cash-estimated-cash-flow-fixed')) { window.CashPurchaseCalculator.toggleComputation('cash-estimated-cash-flow-fixed-computation'); } else if (button.closest('.ir-cash-cash-flow-item') && button.closest('.ir-cash-cash-flow-item').querySelector('.ir-cash-estimated-cash-flow-total')) { window.CashPurchaseCalculator.toggleComputation('cash-estimated-cash-flow-total-computation'); } // ROI section buttons else if (button.closest('.ir-cash-roi-item')) { const item = button.closest('.ir-cash-roi-item'); if (item.querySelector('.ir-cash-annual-appreciation')) { window.CashPurchaseCalculator.toggleComputation('cash-annual-appreciation-computation'); } else if (item.querySelector('.ir-cash-appreciation')) { window.CashPurchaseCalculator.toggleComputation('cash-appreciation-computation'); } else if (item.querySelector('.ir-cash-selling-expenses')) { window.CashPurchaseCalculator.toggleComputation('cash-selling-expenses-computation'); } else if (item.querySelector('.ir-cash-accumulated-cash-flow')) { window.CashPurchaseCalculator.toggleComputation('cash-accumulated-cash-flow-computation'); } else if (item.querySelector('.ir-cash-total-projected-profit')) { window.CashPurchaseCalculator.toggleComputation('cash-total-projected-profit-computation'); } else if (item.querySelector('.ir-cash-annual-cash-on-cash-fixed')) { window.CashPurchaseCalculator.toggleComputation('cash-annual-cash-on-cash-fixed-computation'); } else if (item.querySelector('.ir-cash-annual-cash-on-cash-total')) { window.CashPurchaseCalculator.toggleComputation('cash-annual-cash-on-cash-total-computation'); } else if (item.querySelector('.ir-cash-annual-roi')) { window.CashPurchaseCalculator.toggleComputation('cash-annual-roi-computation'); } else if (item.querySelector('.ir-cash-annual-cap-rate')) { window.CashPurchaseCalculator.toggleComputation('cash-annual-cap-rate-computation'); } } // Tax Benefits section buttons else if (button.closest('.ir-cash-tax-benefits-item')) { window.CashPurchaseCalculator.toggleComputation('cash-annual-depreciation-computation'); } // Cash Reserves section buttons else if (button.closest('.ir-cash-cash-reserves-item') && button.closest('.ir-cash-cash-reserves-item').querySelector('.ir-cash-cash-reserves-value')) { window.CashPurchaseCalculator.toggleComputation('cash-cash-reserves-computation'); } } } if (e.target.matches('.ir-cash-frequency-btn')) { const tabContent = getCashPurchaseTabContent(); if (!tabContent) return; const container = e.target.closest('.ir-cash-fixed-expenses-container, .ir-cash-variable-expenses-container, .ir-cash-cash-flow-container'); if (!container || !tabContent.contains(container)) return; // Get current and new frequency const firstActiveBtn = tabContent.querySelector('.ir-cash-frequency-btn.ir-cash-active'); const currentFrequency = firstActiveBtn?.dataset.frequency || 'monthly'; const newFrequency = e.target.dataset.frequency; // Don't process if clicking the same button if (currentFrequency === newFrequency) return; // Update ALL frequency buttons across all tabs to match the new frequency tabContent.querySelectorAll('.ir-cash-frequency-btn').forEach(function(b) { if (b.dataset.frequency === newFrequency) { b.classList.add('ir-cash-active'); } else { b.classList.remove('ir-cash-active'); } }); // Get all input fields that need conversion const taxesInput = tabContent.querySelector('.ir-cash-taxes'); const insuranceInput = tabContent.querySelector('.ir-cash-insurance'); const utilitiesInput = tabContent.querySelector('.ir-cash-utilities'); const hoaFeeInput = tabContent.querySelector('.ir-cash-hoa-fee'); const grossRentInput = tabContent.querySelector('.ir-cash-gross-rent'); // Convert values based on frequency change if (currentFrequency === 'monthly' && newFrequency === 'annually') { // Convert monthly to annually (multiply by 12) if (taxesInput) taxesInput.value = (parseFloat(taxesInput.value) * 12).toFixed(2); if (insuranceInput) insuranceInput.value = (parseFloat(insuranceInput.value) * 12).toFixed(2); if (utilitiesInput) utilitiesInput.value = (parseFloat(utilitiesInput.value) * 12).toFixed(2); if (hoaFeeInput) hoaFeeInput.value = (parseFloat(hoaFeeInput.value) * 12).toFixed(2); if (grossRentInput) grossRentInput.value = (parseFloat(grossRentInput.value) * 12).toFixed(2); // Percent inputs remain unchanged; display scales } else if (currentFrequency === 'annually' && newFrequency === 'monthly') { // Convert annually to monthly (divide by 12) if (taxesInput) taxesInput.value = (parseFloat(taxesInput.value) / 12).toFixed(2); if (insuranceInput) insuranceInput.value = (parseFloat(insuranceInput.value) / 12).toFixed(2); if (utilitiesInput) utilitiesInput.value = (parseFloat(utilitiesInput.value) / 12).toFixed(2); if (hoaFeeInput) hoaFeeInput.value = (parseFloat(hoaFeeInput.value) / 12).toFixed(2); if (grossRentInput) grossRentInput.value = (parseFloat(grossRentInput.value) / 12).toFixed(2); // Percent inputs remain unchanged; display scales } if (window.CashPurchaseCalculator && window.CashPurchaseCalculator.calculate) { window.CashPurchaseCalculator.calculate(); } } }); // Monitor for tab changes and price updates const priceObserver = new MutationObserver(function(mutations) { let shouldRecalculate = false; mutations.forEach(function(mutation) { if (mutation.type === 'attributes' && (mutation.attributeName === 'class' || mutation.attributeName === 'style')) { shouldRecalculate = true; } if (mutation.type === 'childList') { mutation.addedNodes.forEach(function(node) { if (node.nodeType === 1 && (node.classList?.contains('floorplan-price') || node.querySelector?.('.floorplan-price'))) { shouldRecalculate = true; } }); } if (mutation.type === 'characterData') { const target = mutation.target.parentElement; if (target && target.classList && target.classList.contains('floorplan-price')) { shouldRecalculate = true; } } }); if (shouldRecalculate) { setTimeout(function() { // Force recalculation with fresh data if (window.CashPurchaseCalculator && window.CashPurchaseCalculator.calculate) { window.CashPurchaseCalculator.calculate(); } }, 200); } }); // Start observing priceObserver.observe(document.body, { attributes: true, childList: true, characterData: true, subtree: true, attributeFilter: ['class', 'style'] }); if (!window.__irCashPropertyDropdownBound) { window.__irCashPropertyDropdownBound = true; function handleCashPropertyDropdownEvent(e) { const dropdown = e.target && e.target.closest ? e.target.closest('.ir-cash-property-dropdown') : null; if (!dropdown) return; applySelectedCashProperty(dropdown); } document.addEventListener('change', handleCashPropertyDropdownEvent, true); document.addEventListener('blur', handleCashPropertyDropdownEvent, true); document.addEventListener('input', handleCashPropertyDropdownEvent, true); } const cashPropertyDropdownObserver = new MutationObserver((mutations) => { let shouldRepair = false; mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType !== 1) return; if ( node.classList?.contains('ir-cash-property-dropdown') || node.querySelector?.('.ir-cash-property-dropdown') ) { shouldRepair = true; } }); }); if (shouldRepair) { repairMorganRidgeOptions(document); } }); cashPropertyDropdownObserver.observe(document.body, { childList: true, subtree: true });