@@ -48,7 +48,7 @@ | |||
<section class="form inputs"> | |||
<h3>Property Details</h3> | |||
<label>Price ($)</label> | |||
<input :value="estimate.price" @input="(e) => $emit('update:price', strip(e))"> | |||
<input :value="estimate.price/100" @input="(e) => $emit('update:price', strip(e))"> | |||
<label>Type</label> | |||
<select id="" name="" | |||
:value="estimate.property" | |||
@@ -113,7 +113,7 @@ | |||
<label>Total DTI (%) - Optional</label> | |||
<input :value="loan.dti" @input="(e) => $emit('update:dti', e)"> | |||
<label>Home Owner's Association ($/month)</label> | |||
<input :value="loan.hoa" | |||
<input :value="loan.hoa / 100" | |||
@input="(e) => { $emit('update:hoa', strip(e)) }"> | |||
<label>Interest Rate (%)</label> | |||
@@ -127,14 +127,14 @@ | |||
<input :value="loan.hazardEscrow" | |||
@input="(e) => { $emit('update:hazardEscrow', stripInt(e)) }"> | |||
<label>Hazard Insurance ($/month)</label> | |||
<input :value="loan.hazard" | |||
<input :value="loan.hazard / 100" | |||
@input="(e) => $emit('update:hazard', strip(e))"> | |||
<label>Real Estate Tax Escrow (months)</label> | |||
<input :value="loan.taxEscrow" | |||
@input="e => $emit('update:taxEscrow', stripInt(e))"> | |||
<label>Real Estate Tax ($/month)</label> | |||
<input :value="loan.tax" | |||
<input :value="loan.tax/100" | |||
@input="(e) => $emit('update:tax', strip(e))"> | |||
</section> | |||
@@ -144,7 +144,8 @@ | |||
:key="fee.name + indx" class="fee" | |||
> | |||
<label> | |||
${{fee.amount}}{{ fee.perc ? ` ${fee.perc}%` : ''}} - {{fee.name}}<br> | |||
${{(fee.amount / 100).toFixed(2)}} | |||
{{ fee.perc ? ` ${fee.perc}%` : ''}} - {{fee.name}}<br> | |||
{{fee.type}} | |||
</label> | |||
<img width="21" height="21" src="/assets/image/icon/x-red.svg" | |||
@@ -37,26 +37,26 @@ class="bi bi-plus" viewBox="0 0 16 16"> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 | |||
@update:property="(p) => estimate.property = p" | |||
@update:loanType="(lt) => loan.type.id = lt" | |||
@update:term="(lt) => loans[sel].term = lt" | |||
@update:term="(lt) => loan.term = lt" | |||
@update:program="(p) => loans[sel].program = p" | |||
@update:ltv="setLtv" | |||
@update:amount="setAmount" | |||
@update:housingDti="setHousingDti" | |||
@update:dti="setDti" | |||
@update:hoa="(hoa) => loans[sel].hoa = hoa" | |||
@update:interest="(i) => loans[sel].interest = i" | |||
@update:hoa="(hoa) => loan.hoa = hoa*100" | |||
@update:interest="(i) => loan.interest = i" | |||
@update:interestDays="(d) => loans[sel].interestDays = d" | |||
@update:hazardEscrow="(h) => loans[sel].hazardEscrow = h" | |||
@update:hazard="(h) => loans[sel].hazard = h" | |||
@update:hazard="(h) => loan.hazard = h * 100" | |||
@update:taxEscrow="(t) => loans[sel].taxEscrow = t" | |||
@update:tax="(t) => loans[sel].tax = t" | |||
@update:manualMI="perc => loans[sel].mi.rate = perc" | |||
@update:tax="(t) => loan.tax = t*100" | |||
@update:manualMI="perc => loan.mi.rate = perc" | |||
@toggle:manualMIMonthly= | |||
"() => loans[sel].mi.monthly = !loans[sel].mi.monthly" | |||
@continue="generate" | |||
/> | |||
<loan-summary v-if="hash == '#new/summary'" | |||
:loan="loan" :downpayment="estimate.price - loan.amount" :token="token" :estimate="estimate"/> | |||
:loan="loan" :downpayment="estimate.price - loan.amount" :token="token" :estimate="estimate"/> | |||
</div> | |||
</template> | |||
@@ -81,7 +81,7 @@ const example = { | |||
hazardEscrow: 0, // Hazard insurance escrow in months (0 is none) | |||
tax: 0, // Real Estate taxes monthly payment | |||
taxEscrow: 0, // Months to escrow (0 is none) | |||
hoa: 100, // Home owner's association monthly fee | |||
hoa: 10000, // Home owner's association monthly fee | |||
program: "", | |||
pud: true, // Property under development | |||
zip: '', | |||
@@ -133,7 +133,7 @@ function del() { | |||
// Updates the property price for all loans and their fee amounts. | |||
function setPrice(value) { | |||
this.estimate.price = value | |||
this.estimate.price = Math.round(value*100) | |||
this.estimate.loans[this.sel].fees.forEach(fee => { | |||
if (fee.perc) fee.amount = (fee.perc / 100 * value).toFixed(2) | |||
}) | |||
@@ -151,21 +151,21 @@ function setLtv(e) { | |||
this.loan.ltv = ltv | |||
let num = ltv / 100 * this.estimate.price | |||
this.loan.amount = Math.round(num*100) | |||
this.loan.amount = Math.round(num) | |||
} | |||
// Changes loan.amount\'s <input> and data() values, then syncs with data.ltv | |||
// Loan amount is in cents but LTV is in decimals so some rounding needs to be done. | |||
function setAmount(e) { | |||
let amount = strip(e) | |||
let amount = strip(e) * 100 | |||
if (!this.estimate.price) return | |||
if (amount > this.loan.price) amount = this.loan.price | |||
if (amount > this.estimate.price) amount = this.estimate.price | |||
if (amount < 0) amount = 0 | |||
this.loan.amount = Math.round(amount * 100) | |||
let num = amount / this.estimate.price * 100 | |||
this.loan.ltv = Math.round(num*100) / 100 | |||
this.loan.amount = Math.round(amount) | |||
let num = amount / this.estimate.price | |||
this.loan.ltv = Math.round(num*100) | |||
} | |||
function setDti(e) { | |||
@@ -1,22 +1,23 @@ | |||
<template> | |||
<div v-if="downpayment && totalMonthly > 0"> | |||
<div v-if="valid"> | |||
<section class="form inputs"> | |||
<h3>Monthly Payment - ${{totalMonthly}}</h3> | |||
<label>Loan payment: ${{loanPayment.toFixed(2)}}</label> | |||
<h3>Monthly Payment - ${{format(totalMonthly)}}</h3> | |||
<label>Loan payment: ${{format(loanPayment)}}</label> | |||
<label v-if="loan.mi.monthly"> | |||
Mortgage insurance: ${{(loan.amount*loan.mi.rate/100/12).toFixed(2)}} | |||
Mortgage insurance: ${{format(loan.amount*loan.mi.rate/100/12)}} | |||
</label> | |||
<label>Property taxes: ${{loan.tax}}</label> | |||
<label>Property taxes: ${{format(loan.tax)}}</label> | |||
<label>Homeowner's Insurance: ${{format(loan.hoa)}}</label> | |||
</section> | |||
<section class="form inputs"> | |||
<h3>Cash to Close - ${{totalMonthly}}</h3> | |||
<label>Closing costs: ${{fees}}</label> | |||
<h3>Cash to Close - ${{format(cashToClose)}}</h3> | |||
<label>Closing costs: ${{format(fees)}}</label> | |||
<label v-if="credits">Credits: ${{credits}}</label> | |||
<label>Down payment: ${{downpayment.toFixed(2)}}</label> | |||
<label>Down payment: ${{format(downpayment)}}</label> | |||
<label v-if="!loan.mi.monthly"> | |||
Mortgage insurance: ${{(loanPayment*loan.mi.rate/100).toFixed(2)}} | |||
Mortgage insurance: ${{format(loan.amount*loan.mi.rate/100)}} | |||
</label> | |||
</section> | |||
@@ -30,7 +31,8 @@ | |||
<script setup> | |||
import { ref, computed, onMounted } from 'vue' | |||
const props = defineProps(['downpayment', 'loan', 'valid', 'token', 'estimate']) | |||
let valid = ref(false) | |||
const props = defineProps(['downpayment', 'loan', 'token', 'estimate']) | |||
function amortize(principle, rate, periods) { | |||
return principle * rate*(1+rate)**periods / ((1+rate)**periods - 1) | |||
@@ -38,43 +40,46 @@ function amortize(principle, rate, periods) { | |||
const loanPayment = computed(() => { | |||
let amount = props.loan.amount | |||
if (!props.loan.mi.monthly) amount = | |||
amount + props.loan.mi.rate/100*(amount+props.downpayment) | |||
return amortize(props.loan.amount, | |||
props.loan.interest / 100 / 12, | |||
props.loan.term*12) | |||
}) | |||
const totalMonthly = computed ( | |||
() => (loanPayment.value + | |||
const totalMonthly = computed (() => { | |||
let total = loanPayment.value + | |||
props.loan.tax + | |||
props.loan.hoa + | |||
props.loan.hazard).toFixed(2) | |||
) | |||
const totalCash = computed ( | |||
() => (fees.value + | |||
credits.value + | |||
props.downpayment).toFixed(2) | |||
) | |||
props.loan.hazard | |||
if (props.loan.mi.monthly) { | |||
total = total + props.loan.mi.rate/100*(props.loan.amount) | |||
} | |||
return total | |||
}) | |||
// Closing costs | |||
const fees = computed(() => { | |||
return props.loan.fees.reduce((total, x) => { | |||
return x.amount > 0 ? total + x.amount : 0 | |||
}, 0 | |||
).toFixed(2) | |||
) | |||
}) | |||
const credits = computed(() => { | |||
return props.loan.fees.reduce((total, x) => { | |||
return x.amount < 0 ? total + x.amount : 0 | |||
}, 0 | |||
).toFixed(2) | |||
) | |||
}) | |||
const cashToClose = computed(() => { | |||
return fees + credits + downpayment | |||
let total = fees.value + credits.value + props.downpayment | |||
if (!props.loan.mi.monthly) { | |||
total = total + props.loan.mi.rate/100*(props.loan.amount) | |||
} | |||
return total | |||
}) | |||
function validate() { | |||
@@ -87,15 +92,21 @@ function validate() { | |||
}, | |||
}).then(resp => { | |||
if (resp.ok && resp.status == 200) { | |||
valid.value = true | |||
return | |||
} else { | |||
// resp.text().then(t => this.errors = [t]) | |||
window.location.hash = 'new' | |||
// window.location.hash = 'new' | |||
} | |||
}) | |||
} | |||
// Print number of cents as a nice string of dollars | |||
function format(num) { | |||
return (num/100).toFixed(2) | |||
} | |||
onMounted(() => {validate()}) | |||
</script> |
@@ -94,7 +94,7 @@ type Loan struct { | |||
Ltv float32 `json:"ltv"` | |||
Dti float32 `json:"dti"` | |||
Hoi int `json:"hoi"` | |||
Interest int `json:"interest"` | |||
Interest float32 `json:"interest"` | |||
Mi MI `json:"mi"` | |||
Fees []Fee `json:"fees"` | |||
Name string `json:"name"` | |||