From 46b228c046648fd8c7ef5497d070078045d75d17 Mon Sep 17 00:00:00 2001 From: meskio <meskio@sindominio.net> Date: Sun, 27 Sep 2020 20:16:32 +0200 Subject: [PATCH] Work with transactions --- api/api.go | 11 +++-- api/member_test.go | 2 +- api/purchase.go | 111 +++++++------------------------------------ api/purchase_test.go | 28 +++++------ api/transaction.go | 94 ++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 114 deletions(-) create mode 100644 api/transaction.go diff --git a/api/api.go b/api/api.go index 21006aa..6c32111 100644 --- a/api/api.go +++ b/api/api.go @@ -19,7 +19,7 @@ func initDB(dbPath string) (*gorm.DB, error) { return nil, err } - db.AutoMigrate(&Member{}, &Product{}, &PurchasedProduct{}, &Purchase{}) + db.AutoMigrate(&Member{}, &Product{}, &Purchase{}, &Transaction{}) return db, err } @@ -41,7 +41,7 @@ func Init(dbPath string, signKey string, r *mux.Router) error { r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.GetMember)).Methods("GET") r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.UpdateMember)).Methods("PUT") r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.DeleteMember)).Methods("DELETE") - r.HandleFunc("/member/{num:[0-9]+}/purchase", a.auth(a.GetMemberPurchases)).Methods("GET") + r.HandleFunc("/member/{num:[0-9]+}/purchase", a.auth(a.GetMemberTransactions)).Methods("GET") r.HandleFunc("/product", a.auth(a.ListProducts)).Methods("GET") r.HandleFunc("/product", a.auth(a.AddProduct)).Methods("POST") @@ -49,9 +49,10 @@ func Init(dbPath string, signKey string, r *mux.Router) error { r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.UpdateProduct)).Methods("PUT") r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.DeleteProduct)).Methods("DELETE") - r.HandleFunc("/purchase", a.auth(a.ListPurchases)).Methods("GET") + r.HandleFunc("/transaction", a.auth(a.ListTransactions)).Methods("GET") + r.HandleFunc("/transaction/{id:[0-9]+}", a.auth(a.GetTransaction)).Methods("GET") + r.HandleFunc("/transaction/mine", a.authNum(a.getTransactionsByMember)).Methods("GET") + r.HandleFunc("/purchase", a.authNum(a.AddPurchase)).Methods("POST") - r.HandleFunc("/purchase/{id:[0-9]+}", a.auth(a.GetPurchase)).Methods("GET") - r.HandleFunc("/purchase/mine", a.authNum(a.getPurchasesByMember)).Methods("GET") return nil } diff --git a/api/member_test.go b/api/member_test.go index c000462..679970c 100644 --- a/api/member_test.go +++ b/api/member_test.go @@ -9,7 +9,7 @@ var testMember = Member{ Num: 10, Name: "foo", Email: "foo@example.com", - Balance: 2000, + Balance: 10000, } func TestMemberAddList(t *testing.T) { diff --git a/api/purchase.go b/api/purchase.go index b2cfa41..d142ed1 100644 --- a/api/purchase.go +++ b/api/purchase.go @@ -4,58 +4,30 @@ import ( "encoding/json" "log" "net/http" - "strconv" "time" - "github.com/gorilla/mux" "gorm.io/gorm" ) type Purchase struct { - gorm.Model - MemberNum int `json:"member" gorm:"column:member"` - Member Member `json:"-" gorm:"foreignKey:MemberNum;references:Num"` - Date time.Time `json:"date"` - Total int `json:"total"` - Products []PurchasedProduct `json:"products"` -} - -type PurchasedProduct struct { - gorm.Model `json:"-"` - PurchaseID int `json:"-" gorm:"column:purchase"` - ProductCode int `json:"product" gorm:"column:product"` - Product Product `gorm:"foreignKey:ProductCode;references:Code"` - Price int `json:"price"` - Ammount int `json:"ammount"` -} - -func (a *api) ListPurchases(w http.ResponseWriter, req *http.Request) { - var purchases []Purchase - err := a.db.Preload("Products.Product").Find(&purchases).Error - if err != nil { - log.Printf("Can't list purchases: %v", err) - w.WriteHeader(http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - err = json.NewEncoder(w).Encode(purchases) - if err != nil { - log.Printf("Can't encode purchases: %v", err) - w.WriteHeader(http.StatusInternalServerError) - } + gorm.Model `json:"-"` + TransactionID int `json:"-" gorm:"column:transaction"` + ProductCode int `json:"product" gorm:"column:product"` + Product Product `gorm:"foreignKey:ProductCode;references:Code"` + Price int `json:"price"` + Ammount int `json:"ammount"` } func (a *api) AddPurchase(num int, w http.ResponseWriter, req *http.Request) { - var products []PurchasedProduct - err := json.NewDecoder(req.Body).Decode(&products) + var purchase []Purchase + err := json.NewDecoder(req.Body).Decode(&purchase) if err != nil { log.Printf("Can't create purchase: %v", err) w.WriteHeader(http.StatusInternalServerError) return } total := 0 - for i, p := range products { + for i, p := range purchase { var product Product err = a.db.Where("code = ?", p.ProductCode).First(&product).Error if err != nil { @@ -65,7 +37,7 @@ func (a *api) AddPurchase(num int, w http.ResponseWriter, req *http.Request) { } total += product.Price * p.Ammount - products[i].Price = product.Price + purchase[i].Price = product.Price } var member Member @@ -76,7 +48,7 @@ func (a *api) AddPurchase(num int, w http.ResponseWriter, req *http.Request) { return } if member.Balance < total { - log.Printf("Member %d don't have enough money (%d-%d): %v", num, member.Balance, total, err) + log.Printf("Member %d don't have enough money (%d-%d)", num, member.Balance, total) w.WriteHeader(http.StatusBadRequest) return } @@ -89,20 +61,21 @@ func (a *api) AddPurchase(num int, w http.ResponseWriter, req *http.Request) { return } - purchase := Purchase{ + transaction := Transaction{ MemberNum: num, Date: time.Now(), - Products: products, + Purchase: purchase, + Type: "purchase", Total: total, } - err = a.db.Create(&purchase).Error + err = a.db.Create(&transaction).Error if err != nil { log.Printf("Can't create purchase: %v\n%v", err, purchase) w.WriteHeader(http.StatusInternalServerError) return } - for _, p := range products { + for _, p := range purchase { err := a.db.Model(&Product{}). Where("code = ?", p.ProductCode). Update("stock", gorm.Expr("stock - ?", p.Ammount)).Error @@ -113,60 +86,10 @@ func (a *api) AddPurchase(num int, w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - err = json.NewEncoder(w).Encode(purchase) + err = json.NewEncoder(w).Encode(transaction) if err != nil { log.Printf("Can't encode added purchase: %v", err) w.WriteHeader(http.StatusInternalServerError) } } - -func (a *api) GetPurchase(w http.ResponseWriter, req *http.Request) { - vars := mux.Vars(req) - var purchase Purchase - err := a.db.Where("id = ?", vars["id"]). - Preload("Products.Product"). - First(&purchase).Error - if err != nil { - if err.Error() == "record not found" { - w.WriteHeader(http.StatusNotFound) - return - } - log.Printf("Can't get purchase %s: %v", vars["code"], err) - w.WriteHeader(http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - err = json.NewEncoder(w).Encode(purchase) - if err != nil { - log.Printf("Can't encode purchase: %v", err) - w.WriteHeader(http.StatusInternalServerError) - return - } -} - -func (a *api) GetMemberPurchases(w http.ResponseWriter, req *http.Request) { - vars := mux.Vars(req) - num, _ := strconv.Atoi(vars["num"]) - a.getPurchasesByMember(num, w, req) -} - -func (a *api) getPurchasesByMember(num int, w http.ResponseWriter, req *http.Request) { - var purchases []Purchase - err := a.db.Where("member = ?", num). - Preload("Products.Product"). - Find(&purchases).Error - if err != nil { - log.Printf("Can't list purchases: %v", err) - w.WriteHeader(http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - err = json.NewEncoder(w).Encode(purchases) - if err != nil { - log.Printf("Can't encode purchases: %v", err) - w.WriteHeader(http.StatusInternalServerError) - } -} diff --git a/api/purchase_test.go b/api/purchase_test.go index 3bf4931..e176e19 100644 --- a/api/purchase_test.go +++ b/api/purchase_test.go @@ -11,7 +11,7 @@ func TestPurchaseAddListMine(t *testing.T) { tapi.addTestMember() tapi.addTestProducts() - products := []PurchasedProduct{ + products := []Purchase{ { ProductCode: testProduct.Code, Ammount: 5, @@ -21,26 +21,26 @@ func TestPurchaseAddListMine(t *testing.T) { if resp.StatusCode != http.StatusCreated { t.Fatal("Can't create purchase:", resp.Status) } - var purchases []Purchase - resp = tapi.do("GET", "/purchase/mine", nil, &purchases) + var transactions []Transaction + resp = tapi.do("GET", "/transaction/mine", nil, &transactions) if resp.StatusCode != http.StatusOK { - t.Fatal("Can't get purchases:", resp.Status) + t.Fatal("Can't get transactions:", resp.Status) } - if len(purchases) != 1 { - t.Fatal("Wrong number of purchases", len(purchases), purchases) + if len(transactions) != 1 { + t.Fatal("Wrong number of transactions", len(transactions), transactions) } - if purchases[0].Total != testProduct.Price*products[0].Ammount { - t.Error("Wrong total:", purchases[0].Total) + if transactions[0].Total != testProduct.Price*products[0].Ammount { + t.Error("Wrong total:", transactions[0].Total) } - if len(purchases[0].Products) != 1 { - t.Fatal("Wrong number of products", len(purchases[0].Products), purchases[0].Products) + if len(transactions[0].Purchase) != 1 { + t.Fatal("Wrong number of products", len(transactions[0].Purchase), transactions[0].Purchase) } - if purchases[0].Products[0].ProductCode != testProduct.Code { - t.Error("Wrong product code:", purchases[0].Products[0].ProductCode) + if transactions[0].Purchase[0].ProductCode != testProduct.Code { + t.Error("Wrong product code:", transactions[0].Purchase[0].ProductCode) } - if purchases[0].Products[0].Price != testProduct.Price { - t.Error("Wrong product price:", purchases[0].Products[0].Price) + if transactions[0].Purchase[0].Price != testProduct.Price { + t.Error("Wrong product price:", transactions[0].Purchase[0].Price) } var product Product diff --git a/api/transaction.go b/api/transaction.go new file mode 100644 index 0000000..ee73e30 --- /dev/null +++ b/api/transaction.go @@ -0,0 +1,94 @@ +package api + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + "time" + + "github.com/gorilla/mux" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type Transaction struct { + gorm.Model + MemberNum int `json:"-" gorm:"column:member"` + Member Member `json:"member" gorm:"foreignKey:MemberNum;references:Num"` + Date time.Time `json:"date"` + Total int `json:"total"` + Type string `json:"type"` + + Purchase []Purchase `json:"purchase"` +} + +func (a *api) ListTransactions(w http.ResponseWriter, req *http.Request) { + var transactions []Transaction + err := a.db.Preload("Purchase.Product"). + Preload(clause.Associations). + Find(&transactions).Error + if err != nil { + log.Printf("Can't list transactions: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(transactions) + if err != nil { + log.Printf("Can't encode transactions: %v", err) + w.WriteHeader(http.StatusInternalServerError) + } +} + +func (a *api) GetTransaction(w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + var transaction Transaction + err := a.db.Preload("Purchase.Product"). + Preload(clause.Associations). + First(&transaction, vars["id"]).Error + if err != nil { + if err.Error() == "record not found" { + w.WriteHeader(http.StatusNotFound) + return + } + log.Printf("Can't get transaction %s: %v", vars["code"], err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(transaction) + if err != nil { + log.Printf("Can't encode transaction: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +func (a *api) GetMemberTransactions(w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + num, _ := strconv.Atoi(vars["num"]) + a.getTransactionsByMember(num, w, req) +} + +func (a *api) getTransactionsByMember(num int, w http.ResponseWriter, req *http.Request) { + var transactions []Transaction + err := a.db.Where("member = ?", num). + Preload("Purchase.Product"). + Preload(clause.Associations). + Find(&transactions).Error + if err != nil { + log.Printf("Can't list transactions: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(transactions) + if err != nil { + log.Printf("Can't encode transactions: %v", err) + w.WriteHeader(http.StatusInternalServerError) + } +} -- GitLab