Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

new.vue 6.4 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <template>
  2. <div id="new" class="page">
  3. <h2>New Loan</h2>
  4. <section class="loans-list">
  5. <h3 v-for="(l, indx) in estimate.loans"
  6. :class="sel == indx ? 'sel' : ''"
  7. @click="() => sel = indx"
  8. >
  9. {{l.title}}
  10. </h3>
  11. <div class="add">
  12. <svg @click="create"
  13. xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
  14. class="bi bi-plus" viewBox="0 0 16 16"> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0
  15. 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/> </svg>
  16. </div>
  17. </section>
  18. <loan-details v-if="hash == '#new'"
  19. :estimate="estimate"
  20. :loans="estimate.loans"
  21. :sel="sel"
  22. :loan="loan"
  23. :token="token"
  24. @update:name="(name) => loan.title = name"
  25. @del="del"
  26. @update:borrowerNum="(b) => estimate.borrower.num = b"
  27. @update:borrowerCredit="(c) => estimate.borrower.credit = c"
  28. @update:borrowerIncome="(m) => estimate.borrower.income = Math.round(m*100)"
  29. @update:transaction="(t) => estimate.transaction = t"
  30. @update:occupancy="(t) => estimate.occupancy = t"
  31. @update:price="setPrice"
  32. @update:property="(p) => estimate.property = p"
  33. @update:loanType="changeLoanType"
  34. @update:term="(lt) => loan.term = lt"
  35. @update:program="(p) => loan.program = p"
  36. @update:ltv="setLtv"
  37. @update:amount="setAmount"
  38. @update:housingDti="setHousingDti"
  39. @update:dti="setDti"
  40. @update:hoi="(hoi) => loan.hoi = Math.round(hoi*100)"
  41. @update:interest="(i) => loan.interest = i"
  42. @update:interestDays="(d) => estimate.loans[sel].interestDays = d"
  43. @update:hazardEscrow="(h) => estimate.loans[sel].hazardEscrow = h"
  44. @update:hazard="(h) => loan.hazard = Math.round(h * 100)"
  45. @update:taxEscrow="(t) => estimate.loans[sel].taxEscrow = t"
  46. @update:tax="(t) => loan.tax = Math.round(t*100)"
  47. @update:manualMI="perc => loan.mi.rate = perc"
  48. @toggle:manualMIMonthly=
  49. "() => estimate.loans[sel].mi.monthly = !estimate.loans[sel].mi.monthly"
  50. @continue="generate"
  51. />
  52. <loan-summary v-if="hash == '#new/summary'"
  53. :loan="loan"
  54. :downpayment="estimate.price - loan.amount"
  55. :token="token"
  56. :estimate="estimate"
  57. @update="(e) => estimate = e" />
  58. </div>
  59. </template>
  60. <script>
  61. import { stripLetters, strip, stripInt, stripPerc } from "../../helpers.js"
  62. import LoanDetails from "./details.vue"
  63. import LoanSummary from "./summary.vue"
  64. // The default values of a new loan
  65. const example = {
  66. title: "Example",
  67. type: {},
  68. term: 0,
  69. ltv: 0, // Loan to home value ratio
  70. dti: 0,
  71. housingDti: 0,
  72. amount: 0,
  73. interest: 0,
  74. interestDays: 0,
  75. hazard: 0, // Hazard insurance monthly payment
  76. hazardEscrow: 0, // Hazard insurance escrow in months (0 is none)
  77. tax: 0, // Real Estate taxes monthly payment
  78. taxEscrow: 0, // Months to escrow (0 is none)
  79. hoi: 10000, // Home owner's association monthly fee
  80. program: "",
  81. pud: true, // Property under development
  82. zip: '',
  83. fees: [],
  84. mi: { monthly: false, rate: 0 }
  85. }
  86. // The default loans on a new estimate
  87. const loans = [
  88. Object.assign({mi: { monthly: false, rate: 0 }, type: {}}, example)
  89. ]
  90. // Default estimate fields
  91. let estimate = {
  92. property: "",
  93. transaction: "",
  94. occupancy: "",
  95. price: 0,
  96. borrower: {num: 0, credit: 0, income: 0},
  97. loans: loans,
  98. }
  99. function loan() {
  100. return this.estimate.loans[this.sel]
  101. }
  102. // Clone loan from initial example as a new loan
  103. function create() {
  104. this.estimate.loans.push(
  105. Object.assign(
  106. {mi: { monthly: false, rate: 0 }, type: {}},
  107. example, {fees: this.createFees()}
  108. )
  109. )
  110. }
  111. function createFees() {
  112. return this.fees.map(f => Object.assign({}, f))
  113. }
  114. function del() {
  115. if (this.estimate.loans.length > 1) {
  116. let x = this.sel
  117. this.sel = 0
  118. this.estimate.loans.splice(x, 1)
  119. }
  120. }
  121. // Updates the property price for all loans and their fee amounts.
  122. function setPrice(value) {
  123. this.estimate.price = Math.round(value*100)
  124. this.estimate.loans[this.sel].fees.forEach(fee => {
  125. if (fee.perc) fee.amount = Math.round(fee.perc * value)
  126. })
  127. this.estimate.loans.forEach(l => {l.ltv = 0; l.amount = 0})
  128. }
  129. // Changes loan.ltv's <input> and data() values, then syncs with data.amount
  130. function setLtv(e) {
  131. let ltv = strip(e)
  132. if (!this.estimate.price) return
  133. if (ltv > 100) ltv = 100
  134. if (ltv < 0) ltv = 0
  135. this.loan.ltv = ltv
  136. let num = ltv / 100 * this.estimate.price
  137. this.loan.amount = Math.round(num)
  138. }
  139. // Changes loan.amount\'s <input> and data() values, then syncs with data.ltv
  140. // Loan amount is in cents but LTV is in decimals so some rounding needs to be done.
  141. function setAmount(e) {
  142. let amount = strip(e) * 100
  143. if (!this.estimate.price) return
  144. if (amount > this.estimate.price) amount = this.estimate.price
  145. if (amount < 0) amount = 0
  146. this.loan.amount = Math.round(amount)
  147. let num = amount / this.estimate.price
  148. this.loan.ltv = Math.round(num*100)
  149. }
  150. function setDti(e) {
  151. let dti = strip(e)
  152. let loan = this.estimate.loans[this.sel]
  153. if (!loan.price) return
  154. if (dti > 100) dti = 100
  155. if (dti < 0) dti = 0
  156. e.target.value = dti
  157. loan.dti = dti
  158. }
  159. function setHousingDti(e) {
  160. let housingDti = strip(e)
  161. let loan = this.estimate.loans[this.sel]
  162. if (!loan.price) return
  163. if (housingDti > 100) housingDti = 100
  164. if (housingDti < 0) housingDti = 0
  165. e.target.value = housingDti
  166. loan.housingDti = housingDti
  167. }
  168. function changeLoanType(id) {
  169. if (id == this.loan.type.id) return
  170. // Set mandatory upfront MIP in fees if type is FHA
  171. let i = this.loan.fees.findIndex(
  172. l => l.required && l.name == "FHA Upfront MIP"
  173. )
  174. if (id == 2) {
  175. this.loan.fees.push({
  176. amount: Math.round(this.estimate.price*1.75/100),
  177. perc: 1.75,
  178. name: "FHA Upfront MIP",
  179. type: "Required",
  180. id: 0,
  181. required: true
  182. })
  183. } else if (i >= 0) {
  184. this.loan.fees.splice(i, 1)
  185. }
  186. this.loan.type.id = id
  187. }
  188. function generate() {
  189. window.location.hash = 'new/summary'
  190. }
  191. // Percentage values of fees always take precedent over amounts. The conversion
  192. // happens in setPrice()
  193. export default {
  194. components: { LoanSummary, LoanDetails },
  195. methods: {
  196. generate, createFees, del, create, setPrice, setLtv, setAmount,
  197. setDti, setHousingDti, changeLoanType
  198. },
  199. computed: { loan },
  200. props: ['user', 'fees', 'token'],
  201. data() {
  202. return {
  203. estimate: estimate,
  204. sel: 0,
  205. errors: [],
  206. hash: window.location.hash
  207. }
  208. },
  209. created() {
  210. this.estimate.loans.forEach(l => l.fees = this.createFees())
  211. window.addEventListener("hashchange",
  212. () => this.hash = window.location.hash)
  213. }
  214. }
  215. </script>