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