Browse Source

Allow users to delete saved estimates

master
Immanuel Onyeka 1 year ago
parent
commit
27de7ea416
4 changed files with 195 additions and 44 deletions
  1. +25
    -0
      components/estimates.vue
  2. +27
    -12
      migrations/0_29092022_setup_tables.sql
  3. +2
    -1
      migrations/reset.sql
  4. +141
    -31
      skouter.go

+ 25
- 0
components/estimates.vue View File

@@ -55,6 +55,7 @@ ${{(estimate.price / 100).toLocaleString()}}
<label>Cash to close: ${{format(l.result.cashToClose)}}</label> <label>Cash to close: ${{format(l.result.cashToClose)}}</label>
</div> </div>
<button @click="() => download(estimate)">Generate PDF</button> <button @click="() => download(estimate)">Generate PDF</button>
<button @click="() => deleting = true">Delete</button>
<button @click="() => estimate = null">Cancel</button> <button @click="() => estimate = null">Cancel</button>
</div> </div>


@@ -64,6 +65,11 @@ ${{(estimate.price / 100).toLocaleString()}}
:fileName="`estimate-${estimate.id}.pdf`" :url="dlink"> :fileName="`estimate-${estimate.id}.pdf`" :url="dlink">
</DDialog> </DDialog>


<Dialog v-if="deleting" @close="() => deleting = false">
<h3>Are you sure you want to delete this estimate?</h3>
<button @click="() => del(estimate)">Confirm</button>
</Dialog>

</div> </div>
</template> </template>


@@ -71,6 +77,7 @@ ${{(estimate.price / 100).toLocaleString()}}
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import FeeDialog from "./fee-dialog.vue" import FeeDialog from "./fee-dialog.vue"
import DDialog from "./download-dialog.vue" import DDialog from "./download-dialog.vue"
import Dialog from "./dialog.vue"
import { format } from "../helpers.js" import { format } from "../helpers.js"


const props = defineProps(['user', 'fees', 'token']) const props = defineProps(['user', 'fees', 'token'])
@@ -79,6 +86,7 @@ let edit = ref(null)
let estimates = ref([]) let estimates = ref([])
let estimate = ref() let estimate = ref()
let dlink = ref("") let dlink = ref("")
let deleting = ref()


function newFee(fee, isDebit) { function newFee(fee, isDebit) {
if (!isDebit) { if (!isDebit) {
@@ -142,6 +150,23 @@ function getEstimates() {
}) })
} }


function del(e) {
fetch(`/api/estimate`,
{method: 'DELETE',
body: JSON.stringify(e),
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${props.token}`,
},
}).then(response => {
if (response.ok) {
estimates.value = estimates.value.filter(e => e.id != estimate.value.id)
estimate.value = null
deleting.value = false
}
})
}

function download(estimate) { function download(estimate) {
fetch(`/api/pdf`, fetch(`/api/pdf`,
{method: 'POST', {method: 'POST',


+ 27
- 12
migrations/0_29092022_setup_tables.sql View File

@@ -78,18 +78,9 @@ CREATE TABLE loan_type (
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );


CREATE TABLE borrower (
id INT AUTO_INCREMENT,
credit_score SMALLINT NOT NULL,
monthly_income INT NOT NULL,
num TINYINT NOT NULL, /* Number of people borrowing. */
PRIMARY KEY (`id`)
);

CREATE TABLE estimate ( CREATE TABLE estimate (
id INT AUTO_INCREMENT, id INT AUTO_INCREMENT,
user_id INT NOT NULL, user_id INT NOT NULL,
borrower_id INT NOT NULL,
transaction ENUM('Purchase', 'Refinance'), transaction ENUM('Purchase', 'Refinance'),
price INT NOT NULL, price INT NOT NULL,
property ENUM('Single Detached', property ENUM('Single Detached',
@@ -99,10 +90,31 @@ CREATE TABLE estimate (
occupancy ENUM('Primary', 'Secondary', 'Investment'), occupancy ENUM('Primary', 'Secondary', 'Investment'),
zip VARCHAR(10), zip VARCHAR(10),
pud BOOLEAN, /* Property under development */ pud BOOLEAN, /* Property under development */
PRIMARY KEY (`id`)
);

CREATE TABLE borrower (
id INT AUTO_INCREMENT,
credit_score SMALLINT NOT NULL,
monthly_income INT NOT NULL,
num TINYINT NOT NULL, /* Number of people borrowing. */
estimate_id INT UNIQUE NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
FOREIGN KEY (borrower_id) REFERENCES borrower(id)
FOREIGN KEY (estimate_id) REFERENCES estimate(id)
ON DELETE CASCADE
); );


CREATE TABLE estimate_template (
id INT AUTO_INCREMENT,
estimate_id INT UNIQUE NOT NULL,
user_id INT NOT NULL, /* User who created the template */
branch_id INT NOT NULL DEFAULT 0,
/* Specific branch allowed to use it. 0 is only user. */
PRIMARY KEY (`id`),
FOREIGN KEY (estimate_id) REFERENCES estimate(id)
);


CREATE TABLE loan ( CREATE TABLE loan (
id INT AUTO_INCREMENT, id INT AUTO_INCREMENT,
estimate_id INT NOT NULL, estimate_id INT NOT NULL,
@@ -117,8 +129,9 @@ CREATE TABLE loan (
tax INT DEFAULT 0, /* Real estate taxes */ tax INT DEFAULT 0, /* Real estate taxes */
name VARCHAR(30) DEFAULT '', name VARCHAR(30) DEFAULT '',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
FOREIGN KEY (estimate_id) REFERENCES estimate(id),
FOREIGN KEY (estimate_id) REFERENCES estimate(id) ON DELETE CASCADE,
FOREIGN KEY (type_id) REFERENCES loan_type(id) FOREIGN KEY (type_id) REFERENCES loan_type(id)
ON DELETE CASCADE
ON UPDATE RESTRICT ON UPDATE RESTRICT
); );


@@ -137,6 +150,7 @@ CREATE TABLE mi (
initial_amount INT DEFAULT 0, initial_amount INT DEFAULT 0,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
FOREIGN KEY (loan_id) REFERENCES loan(id) FOREIGN KEY (loan_id) REFERENCES loan(id)
ON DELETE CASCADE
); );


/* template = true fees are saved for users or branches. If template or default /* template = true fees are saved for users or branches. If template or default
@@ -153,6 +167,7 @@ CREATE TABLE fee (
category VARCHAR(60), category VARCHAR(60),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
FOREIGN KEY (loan_id) REFERENCES loan(id) FOREIGN KEY (loan_id) REFERENCES loan(id)
ON DELETE CASCADE
); );


/* Templates to be reused by users or branches. Either user_id or branch_id must /* Templates to be reused by users or branches. Either user_id or branch_id must
@@ -185,5 +200,5 @@ CREATE TABLE estimate_result (
total_credits INT NOT NULL, total_credits INT NOT NULL,
cash_to_close INT NOT NULL, cash_to_close INT NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
FOREIGN KEY (loan_id) REFERENCES loan(id)
FOREIGN KEY (loan_id) REFERENCES loan(id) ON DELETE CASCADE
); );

+ 2
- 1
migrations/reset.sql View File

@@ -1,10 +1,11 @@
DROP TABLE IF EXISTS estimate_template;
DROP TABLE IF EXISTS estimate_result; DROP TABLE IF EXISTS estimate_result;
DROP TABLE IF EXISTS mi; DROP TABLE IF EXISTS mi;
DROP TABLE IF EXISTS fee; DROP TABLE IF EXISTS fee;
DROP TABLE IF EXISTS fee_template; DROP TABLE IF EXISTS fee_template;
DROP TABLE IF EXISTS loan; DROP TABLE IF EXISTS loan;
DROP TABLE IF EXISTS estimate;
DROP TABLE IF EXISTS borrower; DROP TABLE IF EXISTS borrower;
DROP TABLE IF EXISTS estimate;
DROP TABLE IF EXISTS loan_type; DROP TABLE IF EXISTS loan_type;
DROP TABLE IF EXISTS license; DROP TABLE IF EXISTS license;
DROP TABLE IF EXISTS user; DROP TABLE IF EXISTS user;


+ 141
- 31
skouter.go View File

@@ -189,6 +189,13 @@ type Estimate struct {
Loans []Loan `json:"loans"` Loans []Loan `json:"loans"`
} }


type ETemplate struct {
Id int `json:"id"`
Estimate Estimate `json:"estimate"`
UserId int `json:"userId"`
BranchId int `json:"branchId"`
}

type Report struct { type Report struct {
Title string Title string
Name string Name string
@@ -574,6 +581,19 @@ func deleteFeeTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
if err != nil { w.WriteHeader(500); return } if err != nil { w.WriteHeader(500); return }
} }


func deleteEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var estimate Estimate
var err error
err = json.NewDecoder(r.Body).Decode(&estimate)
if err != nil { w.WriteHeader(422); return }
claims, err := getClaims(r)
err = estimate.del(db, claims.Id)
if err != nil { http.Error(w, err.Error(), 500); return }
}


func getMi(db *sql.DB, loan int) (MI, error) { func getMi(db *sql.DB, loan int) (MI, error) {
var mi MI var mi MI


@@ -608,25 +628,23 @@ func getMi(db *sql.DB, loan int) (MI, error) {
return mi, nil return mi, nil
} }


func getBorrower(db *sql.DB, id int) (Borrower, error) {
var borrower Borrower
func (estimate *Estimate) getBorrower(db *sql.DB) error {
query := `SELECT id, credit_score, monthly_income, num FROM borrower
WHERE estimate_id = ? LIMIT 1`


row := db.QueryRow(
"SELECT * FROM borrower " +
"WHERE id = ? LIMIT 1",
id)
row := db.QueryRow(query, estimate.Id)


if err := row.Scan( if err := row.Scan(
&borrower.Id,
&borrower.Credit,
&borrower.Income,
&borrower.Num,
&estimate.Borrower.Id,
&estimate.Borrower.Credit,
&estimate.Borrower.Income,
&estimate.Borrower.Num,
) )
err != nil { err != nil {
return borrower, fmt.Errorf("Borrower scanning error: %v", err)
return fmt.Errorf("Borrower scanning error: %v", err)
} }


return borrower, nil
return nil
} }


// Query Lender APIs and parse responses into MI structs // Query Lender APIs and parse responses into MI structs
@@ -1740,6 +1758,14 @@ func getLoans(db *sql.DB, e int, id int) ( []Loan, error ) {
return loans, nil return loans, nil
} }


func getEstimate(db *sql.DB, id int) ( Estimate, error ) {
estimates, err := getEstimates(db, id, 0)
if err != nil { return Estimate{}, err }
return estimates[0], nil
}

func getEstimates(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
@@ -1749,7 +1775,6 @@ func getEstimates(db *sql.DB, id int, user int) ( []Estimate, error ) {
query = `SELECT query = `SELECT
id, id,
user_id, user_id,
borrower_id,
transaction, transaction,
price, price,
property, property,
@@ -1773,7 +1798,6 @@ func getEstimates(db *sql.DB, id int, user int) ( []Estimate, error ) {
if err := rows.Scan( if err := rows.Scan(
&estimate.Id, &estimate.Id,
&estimate.User, &estimate.User,
&estimate.Borrower.Id,
&estimate.Transaction, &estimate.Transaction,
&estimate.Price, &estimate.Price,
&estimate.Property, &estimate.Property,
@@ -1785,11 +1809,10 @@ func getEstimates(db *sql.DB, id int, user int) ( []Estimate, error ) {
return estimates, err return estimates, err
} }
borrower, err := getBorrower(db, estimate.Borrower.Id)
err := estimate.getBorrower(db)
if err != nil { if err != nil {
return estimates, err return estimates, err
} }
estimate.Borrower = borrower
estimates = append(estimates, estimate) estimates = append(estimates, estimate)
} }


@@ -1804,33 +1827,80 @@ func getEstimates(db *sql.DB, id int, user int) ( []Estimate, error ) {
return estimates, nil return estimates, nil
} }


func getETemplate(db *sql.DB, id int, user int) ( []ETemplate, error ) {
var eTemplates []ETemplate
var query string
var rows *sql.Rows
var err error

query = `SELECT
id,
estimate_id,
user_id,
branch_id,
FROM estimate_template WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND
user_id = CASE @e := ? WHEN 0 THEN user_id ELSE @e END
`
rows, err = db.Query(query, id, user)

if err != nil {
return eTemplates, err
}

defer rows.Close()

for rows.Next() {
var e ETemplate

if err := rows.Scan(
&e.Id,
&e.Estimate.Id,
&e.UserId,
&e.BranchId,
)
err != nil {
return eTemplates, err
}
e.Estimate, err = getEstimate(db, e.Estimate.Id)
if err != nil {
return eTemplates, err
}

eTemplates = append(eTemplates, e)
}
return eTemplates, nil
}

// Accepts a borrower struct and returns the id of the inserted borrower and // Accepts a borrower struct and returns the id of the inserted borrower and
// any related error. // any related error.
func insertBorrower(db *sql.DB, borrower Borrower) (int, error) {
func (estimate *Estimate) insertBorrower(db *sql.DB) error {
var query string var query string
var row *sql.Row var row *sql.Row
var err error var err error
var id int // Inserted loan's id


query = `INSERT INTO borrower query = `INSERT INTO borrower
( (
estimate_id,
credit_score, credit_score,
monthly_income, monthly_income,
num num
) )
VALUES (?, ?, ?)
VALUES (?, ?, ?, ?)
RETURNING id RETURNING id
` `
row = db.QueryRow(query, row = db.QueryRow(query,
borrower.Credit,
borrower.Income,
borrower.Num,
estimate.Id,
estimate.Borrower.Credit,
estimate.Borrower.Income,
estimate.Borrower.Num,
) )


err = row.Scan(&id)
if err != nil { return 0, err }
err = row.Scan(&estimate.Borrower.Id)
if err != nil { return err }


return id, nil
return nil
} }


func insertMi(db *sql.DB, mi MI) (int, error) { func insertMi(db *sql.DB, mi MI) (int, error) {
@@ -1968,13 +2038,9 @@ func (estimate *Estimate) insertEstimate(db *sql.DB) (error){
var err error var err error
// var id int // Inserted estimate's id // var id int // Inserted estimate's id
estimate.Borrower.Id, err = insertBorrower(db, estimate.Borrower)
if err != nil { return err }

query = `INSERT INTO estimate query = `INSERT INTO estimate
( (
user_id, user_id,
borrower_id,
transaction, transaction,
price, price,
property, property,
@@ -1982,12 +2048,11 @@ func (estimate *Estimate) insertEstimate(db *sql.DB) (error){
zip, zip,
pud pud
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING id RETURNING id
` `
row = db.QueryRow(query, row = db.QueryRow(query,
estimate.User, estimate.User,
estimate.Borrower.Id,
estimate.Transaction, estimate.Transaction,
estimate.Price, estimate.Price,
estimate.Property, estimate.Property,
@@ -1998,6 +2063,9 @@ func (estimate *Estimate) insertEstimate(db *sql.DB) (error){


err = row.Scan(&estimate.Id) err = row.Scan(&estimate.Id)
if err != nil { return err } if err != nil { return err }
err = estimate.insertBorrower(db)
if err != nil { return err }


for i := range estimate.Loans { for i := range estimate.Loans {
estimate.Loans[i].EstimateId = estimate.Id estimate.Loans[i].EstimateId = estimate.Id
@@ -2008,6 +2076,44 @@ func (estimate *Estimate) insertEstimate(db *sql.DB) (error){
return nil return nil
} }


func (estimate *Estimate) del(db *sql.DB, user int) (error) {
var query string
var err error

query = `DELETE FROM estimate WHERE id = ? AND user_id = ?`
_, err = db.Exec(query, estimate.Id, user)
if err != nil { return err }

return nil
}

func (eTemplate *ETemplate) insert(db *sql.DB) (error) {
var query string
var row *sql.Row
var err error
query = `INSERT INTO estimate_template
(
user_id,
branch_id,
estimate_id,
)
VALUES (?, ?, ?)
RETURNING id
`
row = db.QueryRow(query,
eTemplate.UserId,
eTemplate.BranchId,
eTemplate.Estimate.Id,
)

err = row.Scan(&eTemplate.Id)
if err != nil { return err }

return nil
}

func createEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) { func createEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var estimate Estimate var estimate Estimate
err := json.NewDecoder(r.Body).Decode(&estimate) err := json.NewDecoder(r.Body).Decode(&estimate)
@@ -2383,6 +2489,10 @@ func api(w http.ResponseWriter, r *http.Request) {
r.Method == http.MethodPost && r.Method == http.MethodPost &&
guard(r, 1): guard(r, 1):
createEstimate(w, db, r) createEstimate(w, db, r)
case match(p, "/api/estimate", &args) &&
r.Method == http.MethodDelete &&
guard(r, 1):
deleteEstimate(w, db, r)
case match(p, "/api/estimate/validate", &args) && case match(p, "/api/estimate/validate", &args) &&
r.Method == http.MethodPost && r.Method == http.MethodPost &&
guard(r, 1): guard(r, 1):


Loading…
Cancel
Save