diff --git a/migrations/0_29092022_setup_tables.sql b/migrations/0_29092022_setup_tables.sql index a7d2795..df65f37 100644 --- a/migrations/0_29092022_setup_tables.sql +++ b/migrations/0_29092022_setup_tables.sql @@ -152,3 +152,15 @@ CREATE TABLE fee_template ( FOREIGN KEY (user_id) REFERENCES user(id), FOREIGN KEY (branch_id) REFERENCES branch(id) ); + +CREATE TABLE estimate_result ( + id INT AUTO_INCREMENT, + loan_id INT UNIQUE NOT NULL, + loan_payment INT NOT NULL, + total_monthly INT NOT NULL, + total_fees INT NOT NULL, + total_credits INT NOT NULL, + cash_to_close INT NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (loan_id) REFERENCES loan(id) +); diff --git a/migrations/reset.sql b/migrations/reset.sql index ad69cd5..c3e75b1 100644 --- a/migrations/reset.sql +++ b/migrations/reset.sql @@ -1,3 +1,4 @@ +DROP TABLE IF EXISTS estimate_result; DROP TABLE IF EXISTS mi; DROP TABLE IF EXISTS fee; DROP TABLE IF EXISTS fee_template; diff --git a/migrations/seed.sql b/migrations/seed.sql index a016887..be99c66 100644 --- a/migrations/seed.sql +++ b/migrations/seed.sql @@ -297,3 +297,37 @@ INSERT INTO mi ( 77000, 28 ); + + +INSERT INTO estimate_result ( + loan_id, + loan_payment, + total_monthly, + total_fees, + total_credits, + cash_to_close +) VALUES +( + 1, + 258060, + 311500, + 87211, + -15089, + 411822 +), +( + 2, + 198060, + 241500, + 54400, + -10089, + 310022 +), +( + 3, + 118000, + 350000, + 54400, + -70089, + 411699 +); diff --git a/skouter.go b/skouter.go index ae2fa45..065b629 100644 --- a/skouter.go +++ b/skouter.go @@ -118,12 +118,12 @@ type MI struct { } type Result struct { - Id int `json:"loanId"` + Id int `json:"loanId"` LoanId int `json:"loanId"` LoanPayment int `json:"loanPayment"` TotalMonthly int `json:"totalMonthly"` - Fees int `json:"fees"` - Credits int `json:"credits"` + TotalFees int `json:"totalFees"` + TotalCredits int `json:"totalCredits"` CashToClose int `json:"cashToClose"` } @@ -138,7 +138,7 @@ type Estimate struct { Zip string `json:"zip"` Pud bool `json:"pud"` Loans []Loan `json:"loans"` - Result []Result `json:"result"` + Results []Result `json:"results"` } var ( @@ -210,18 +210,15 @@ func match(path, pattern string, args *[]string) bool { 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 } - +func makeResults(estimate Estimate) ([]Result) { + var results []Result amortize := func(principle float64, rate float64, periods float64) int { exp := math.Pow(1+rate, periods) return int(principle * rate * exp / (exp - 1)) } for _, loan := range estimate.Loans { + var result Result // Monthly payments use amortized loan payment formula plus monthly MI, // plus all other recurring fees result.LoanPayment = amortize(float64(loan.Amount), @@ -235,22 +232,29 @@ func summarize(w http.ResponseWriter, db *sql.DB, r *http.Request) { for i := range loan.Fees { if loan.Fees[i].Amount > 0 { - result.Fees = result.Fees + loan.Fees[i].Amount + result.TotalFees = result.TotalFees + loan.Fees[i].Amount } else { - result.Credits = result.Credits + loan.Fees[i].Amount + result.TotalCredits = result.TotalCredits + loan.Fees[i].Amount } } result.CashToClose = - result.Fees + result.Credits + (estimate.Price - loan.Amount) - + result.TotalFees + result.TotalCredits + (estimate.Price - loan.Amount) + result.LoanId = loan.Id + + results = append(results, result) } - loan := estimate.Loans[0] + return results +} - +func summarize(w http.ResponseWriter, db *sql.DB, r *http.Request) { + var estimate Estimate + err := json.NewDecoder(r.Body).Decode(&estimate) + if err != nil { http.Error(w, "Invalid estimate.", 422); return } + results := makeResults(estimate) - json.NewEncoder(w).Encode(result) + json.NewEncoder(w).Encode(results) } func getLoanType( @@ -685,6 +689,40 @@ func queryUsers(db *sql.DB, id int) ( []User, error ) { return users, nil } +func insertResults(db *sql.DB, results []Result) (error){ + var query string + var row *sql.Row + var err error + + query = `INSERT INTO estimate_result + ( + loan_id, + loan_payment, + total_monthly, + total_fees, + total_credits, + cash_to_close + ) + VALUES (?, ?, ?, ?, ?, ?, ?) + RETURNING id + ` + for i := range results { + db.QueryRow(query, + results[i].LoanId, + results[i].LoanPayment, + results[i].TotalMonthly, + results[i].TotalFees, + results[i].TotalCredits, + results[i].CashToClose, + ) + err = row.Scan(&results[i].Id) + if err != nil { return err } + } + + return nil +} + + func insertUser(db *sql.DB, user User) (User, error){ var query string var row *sql.Row @@ -869,6 +907,57 @@ func queryBorrower(db *sql.DB, id int) ( Borrower, error ) { return borrower, nil } +// Must have an estimate ID 'e', but not necessarily a loan id 'id' +func getResults(db *sql.DB, e int, id int) ( []Result, error ) { + var results []Result + var query string + var rows *sql.Rows + var err error + + query = `SELECT + id, + loan_id, + loan_payment, + total_monthly, + total_fees, + total_credits, + cash_to_close, + FROM estimate_result WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND + loan_id = ( SELECT id FROM loan WHERE loan.estimate_id = ? ) + ` + rows, err = db.Query(query, id, e) + + if err != nil { + return results, err + } + + defer rows.Close() + + for rows.Next() { + var result Result + + if err := rows.Scan( + &result.Id, + &result.LoanId, + &result.LoanPayment, + &result.TotalMonthly, + &result.TotalFees, + &result.TotalCredits, + &result.CashToClose, + ) + err != nil { + return results, err + } + + results = append(results, result) + } + + // Prevents runtime panics + // if len(results) == 0 { return results, errors.New("Result not found.") } + + return results, nil +} + // Must have an estimate ID 'e', but not necessarily a loan id 'id' func getLoans(db *sql.DB, e int, id int) ( []Loan, error ) { var loans []Loan @@ -958,7 +1047,6 @@ func getEstimates(db *sql.DB, id int, user int) ( []Estimate, error ) { ` rows, err = db.Query(query, id, user) - if err != nil { return estimates, err } @@ -982,6 +1070,13 @@ func getEstimates(db *sql.DB, id int, user int) ( []Estimate, error ) { err != nil { return estimates, err } + + results, err := getResults(db, estimate.Id, 0) + if err != nil { + return estimates, err + } + + estimate.Results = results estimates = append(estimates, estimate) } @@ -1196,8 +1291,13 @@ func createEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) { estimate, err = insertEstimate(db, estimate) if err != nil { http.Error(w, err.Error(), 422); return } + estimate.Results = makeResults(estimate) + err = insertResults(db, estimate.Results) + if err != nil { http.Error(w, err.Error(), 500); return } + e, err := getEstimates(db, estimate.Id, 0) + if err != nil { http.Error(w, err.Error(), 500); return } - json.NewEncoder(w).Encode(estimate) + json.NewEncoder(w).Encode(e[0]) } // Query all estimates that belong to the current user