Browse Source

Calculate estimate summary values in backend

master
Immanuel Onyeka 1 year ago
parent
commit
6d98b7b47a
3 changed files with 110 additions and 71 deletions
  1. +24
    -2
      components/estimates.vue
  2. +1
    -3
      components/new/summary.vue
  3. +85
    -66
      skouter.go

+ 24
- 2
components/estimates.vue View File

@@ -33,12 +33,15 @@
<h3>Saved Estimates</h3> <h3>Saved Estimates</h3>


<div class="entry" v-for="e in estimates" v-if="!estimate"> <div class="entry" v-for="e in estimates" v-if="!estimate">
<span @click="() => estimate = e">{{e.id}} - {{e.property}} - ${{e.price/100}}</span> <span @click="() => estimate = e">
{{e.id}} - {{e.property}} - ${{e.price/100}}
</span>
</div> </div>


<div class="details" v-if="estimate"> <div class="details" v-if="estimate">
{{estimate}} {{estimate}}
<label>Name: {{estimate.name}}</label> <label>{{estimate.id}} - {{estimate.property}} - ${{estimate.price / 100}}</label>
<button @click="summarize">test</button>
</div> </div>


</section> </section>
@@ -86,6 +89,25 @@ function getEstimates() {


} }


function summarize() {
fetch(`/api/estimate/summarize`,
{method: 'POST',
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${props.token}`,
},
body: JSON.stringify(estimate.value),
}).then(response => {
if (response.ok) { return response.json() } else {
response.text().then(t => console.log(t))
}
}).then (result => {
if (!result || !result.length) return // Exit if token is invalid or no fees are saved
console.log('done', result)
})

}

onMounted(() => { onMounted(() => {
getEstimates() getEstimates()
}) })


+ 1
- 3
components/new/summary.vue View File

@@ -42,9 +42,7 @@ 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)
} }


const loanPayment = computed(() => { const loanPayment = computed(() => {
let amount = props.loan.amount
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)


+ 85
- 66
skouter.go View File

@@ -16,6 +16,7 @@ import (
"time" "time"
"errors" "errors"
"strings" "strings"
"math"
// pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf" // pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
@@ -94,6 +95,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"`
Hazard int `json:"hazard"`
Tax int `json:"hoi"` Tax int `json:"hoi"`
Interest float32 `json:"interest"` Interest float32 `json:"interest"`
Mi MI `json:"mi"` Mi MI `json:"mi"`
@@ -116,15 +118,15 @@ type MI struct {
} }


type Result struct { type Result struct {
User int `json:"user"` Id int `json:"loanId"`
Borrower Borrower `json:"borrower"` LoanId int `json:"loanId"`
Transaction string `json:"transaction"` LoanPayment int `json:"loanPayment"`
Price int `json:"price"` TotalMonthly int `json:"totalMonthly"`
Property string `json:"property"` Fees int `json:"fees"`
Occupancy string `json:"occupancy"` Credits int `json:"credits"`
CashToClose int `json:"cashToClose"`
Zip string `json:"zip"` Zip string `json:"zip"`
Pud bool `json:"pud"` Pud bool `json:"pud"`
Loans []Loan `json:"loans"`
} }


type Estimate struct { type Estimate struct {
@@ -209,6 +211,41 @@ func match(path, pattern string, args *[]string) bool {
return true return true
} }


func summarize(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var estimate Estimate
var result Result
err := json.NewDecoder(r.Body).Decode(&estimate)
if err != nil { http.Error(w, "Invalid estimate.", 422); return }
amortize := func(principle float64, rate float64, periods float64) int {
exp := math.Pow(1+rate, periods)
return int(principle * rate * exp / (exp - 1))
}
loan := estimate.Loans[0]
result.LoanPayment = amortize(float64(loan.Amount),
float64(loan.Interest / 100 / 12),
float64(loan.Term * 12))
result.TotalMonthly = result.LoanPayment + loan.Hoi + loan.Tax + loan.Hazard
if loan.Mi.Monthly {
result.TotalMonthly = result.TotalMonthly +
int(loan.Mi.Rate/100*float32(loan.Amount))
}
for i := range loan.Fees {
if loan.Fees[i].Amount > 0 {
result.Fees = result.Fees + loan.Fees[i].Amount
} else {
result.Credits = result.Credits + loan.Fees[i].Amount
}
}
result.CashToClose =
result.Fees + result.Credits + (estimate.Price - loan.Amount)
json.NewEncoder(w).Encode(result)
}

func getLoanType( func getLoanType(
db *sql.DB, db *sql.DB,
user int, user int,
@@ -249,10 +286,11 @@ func getLoanType(
func getFees(db *sql.DB, loan int) ([]Fee, error) { func getFees(db *sql.DB, loan int) ([]Fee, error) {
var fees []Fee var fees []Fee


rows, err := db.Query( query := `SELECT id, loan_id, amount, perc, type, notes, name, category
"SELECT * FROM fees " + FROM fee
"WHERE loan_id = ?", WHERE loan_id = ?`
loan) rows, err := db.Query(query, loan)
if err != nil { if err != nil {
return nil, fmt.Errorf("Fee query error %v", err) return nil, fmt.Errorf("Fee query error %v", err)
@@ -340,9 +378,14 @@ func getMi(db *sql.DB, loan int) (MI, error) {
initial_premium, initial_rate, initial_amount initial_premium, initial_rate, initial_amount
FROM mi WHERE loan_id = ?` FROM mi WHERE loan_id = ?`


row := db.QueryRow(query, loan) rows, err := db.Query(query, loan)
if err != nil { return mi, err }
defer rows.Close()


if err := row.Scan( if (!rows.Next()) { return mi, nil }
if err := rows.Scan(
&mi.Type, &mi.Type,
&mi.Label, &mi.Label,
&mi.Lender, &mi.Lender,
@@ -361,53 +404,6 @@ func getMi(db *sql.DB, loan int) (MI, error) {
return mi, nil return mi, nil
} }


func getLoans(db *sql.DB, estimate int) ([]Loan, error) {
var loans []Loan

query := `SELECT
l.id, l.amount, l.term, l.interest, l.ltv, l.dti, l.hoi,
lt.id, lt.user_id, lt.branch_id, lt.name
FROM loan l INNER JOIN loan_type lt ON l.type_id = lt.id
WHERE l.estimate_id = ?
`
rows, err := db.Query(query, estimate)
if err != nil {
return nil, fmt.Errorf("Loan query error %v", err)
}

defer rows.Close()

for rows.Next() {
var loan Loan

if err := rows.Scan(
&loan.Id,
&loan.Amount,
&loan.Term,
&loan.Interest,
&loan.Ltv,
&loan.Dti,
&loan.Hoi,
&loan.Type.Id,
&loan.Type.User,
&loan.Type.Branch,
&loan.Type.Name,
)
err != nil {
return loans, fmt.Errorf("Loans scanning error: %v", err)
}
mi, err := getMi(db, loan.Id)
if err != nil {
return loans, err
}
loan.Mi = mi
loans = append(loans, loan)
}
return loans, nil
}

func getBorrower(db *sql.DB, id int) (Borrower, error) { func getBorrower(db *sql.DB, id int) (Borrower, error) {
var borrower Borrower var borrower Borrower


@@ -867,7 +863,7 @@ func queryBorrower(db *sql.DB, id int) ( Borrower, error ) {
} }


// Must have an estimate ID 'e', but not necessarily a loan id 'id' // Must have an estimate ID 'e', but not necessarily a loan id 'id'
func queryLoan(db *sql.DB, e int, id int) ( []Loan, error ) { func getLoans(db *sql.DB, e int, id int) ( []Loan, error ) {
var loans []Loan var loans []Loan
var query string var query string
var rows *sql.Rows var rows *sql.Rows
@@ -913,6 +909,18 @@ func queryLoan(db *sql.DB, e int, id int) ( []Loan, error ) {
err != nil { err != nil {
return loans, err return loans, err
} }
mi, err := getMi(db, loan.Id)
if err != nil {
return loans, err
}
loan.Mi = mi
fees, err := getFees(db, loan.Id)
if err != nil {
return loans, err
}
loan.Fees = fees
loans = append(loans, loan) loans = append(loans, loan)
} }
@@ -922,7 +930,7 @@ func queryLoan(db *sql.DB, e int, id int) ( []Loan, error ) {
return loans, nil return loans, nil
} }


func queryEstimate(db *sql.DB, id int, user int) ( []Estimate, error ) { func getEstimates(db *sql.DB, id int, user int) ( []Estimate, error ) {
var estimates []Estimate var estimates []Estimate
var query string var query string
var rows *sql.Rows var rows *sql.Rows
@@ -974,7 +982,7 @@ func queryEstimate(db *sql.DB, id int, user int) ( []Estimate, error ) {
if len(estimates) == 0 { return estimates, errors.New("Estimate not found.") } if len(estimates) == 0 { return estimates, errors.New("Estimate not found.") }
for i := range estimates { for i := range estimates {
estimates[i].Loans, err = queryLoan(db, estimates[i].Id, 0) estimates[i].Loans, err = getLoans(db, estimates[i].Id, 0)
if err != nil { return estimates, err } if err != nil { return estimates, err }
} }
@@ -1010,6 +1018,13 @@ func insertBorrower(db *sql.DB, borrower Borrower) (int, error) {
return id, nil return id, nil
} }


func insertFee(db *sql.DB, fee Fee) (int, error) {
query := `INSERT INTO fee
id, loan_id, amount, perc, type, notes, name, category
FROM fee
WHERE loan_id = ?`
}

func insertLoan(db *sql.DB, loan Loan) (Loan, error){ func insertLoan(db *sql.DB, loan Loan) (Loan, error){
var query string var query string
var row *sql.Row var row *sql.Row
@@ -1048,7 +1063,7 @@ func insertLoan(db *sql.DB, loan Loan) (Loan, error){
err = row.Scan(&id) err = row.Scan(&id)
if err != nil { return Loan{}, err } if err != nil { return Loan{}, err }


loans, err := queryLoan(db, id, 0) loans, err := getLoans(db, id, 0)
if err != nil { return Loan{}, err } if err != nil { return Loan{}, err }


return loans[0], nil return loans[0], nil
@@ -1098,7 +1113,7 @@ func insertEstimate(db *sql.DB, estimate Estimate) (Estimate, error){
if err != nil { return estimate, err } if err != nil { return estimate, err }
} }
estimates, err := queryEstimate(db, estimate.Id, 0) estimates, err := getEstimates(db, estimate.Id, 0)
if err != nil { return Estimate{}, err } if err != nil { return Estimate{}, err }


return estimates[0], nil return estimates[0], nil
@@ -1122,7 +1137,7 @@ func fetchEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var estimates []Estimate var estimates []Estimate
claims, err := getClaims(r) claims, err := getClaims(r)


estimates, err = queryEstimate(db, 0, claims.Id) estimates, err = getEstimates(db, 0, claims.Id)
if err != nil { http.Error(w, err.Error(), 500); return } if err != nil { http.Error(w, err.Error(), 500); return }
json.NewEncoder(w).Encode(estimates) json.NewEncoder(w).Encode(estimates)
@@ -1300,6 +1315,10 @@ func api(w http.ResponseWriter, r *http.Request) {
r.Method == http.MethodPost && r.Method == http.MethodPost &&
guard(r, 1): guard(r, 1):
validateEstimate(w, db, r) validateEstimate(w, db, r)
case match(p, "/api/estimate/summarize", &args) &&
r.Method == http.MethodPost &&
guard(r, 1):
summarize(w, db, r)
default: default:
http.Error(w, "Invalid route or token", 404) http.Error(w, "Invalid route or token", 404)
} }


||||||
x
 
000:0
Loading…
Cancel
Save