From f2fcb2b397ec47fe147d32f69f449bef8281b613 Mon Sep 17 00:00:00 2001 From: meskio <meskio@sindominio.net> Date: Fri, 9 Oct 2020 15:07:35 +0200 Subject: [PATCH] Redesign orders in the db --- api/order.go | 73 ++++++++++++++++++++++++++++------------------ api/order_test.go | 36 +++++++++++++---------- api/transaction.go | 28 +++++++++++------- 3 files changed, 82 insertions(+), 55 deletions(-) diff --git a/api/order.go b/api/order.go index 1c05d46..11e68eb 100644 --- a/api/order.go +++ b/api/order.go @@ -16,14 +16,14 @@ type Order struct { gorm.Model Name string `json:"name"` Description string `json:"description"` - MemberNum int `json:"-" gorm:"column:member"` + MemberNum int `json:"member_num" gorm:"column:member"` Member *Member `json:"member,omitempty" gorm:"foreignKey:MemberNum;references:Num"` Deadline time.Time `json:"deadline"` Active bool `json:"active" gorm:"index"` - Products []Product `json:"products" gorm:"many2many:order_products;References:Code;JoinReferences:ProductCode"` - Purchases []OrderPurchase `json:"purchases"` - TransactionID *uint `json:"-" gorm:"column:transaction"` + Products []Product `json:"products" gorm:"many2many:order_products;References:Code;JoinReferences:ProductCode"` + Transactions []Transaction `json:"transactions" gorm:"foreignKey:OrderID"` + TransactionID *uint `json:"-" gorm:"column:transaction"` } type OrderPurchase struct { @@ -31,8 +31,6 @@ type OrderPurchase struct { TransactionID uint `json:"-"` ProductCode int `json:"product_code"` Product *Product `json:"product" gorm:"foreignKey:ProductCode;references:Code"` - OrderID uint `json:"order_id"` - Order *Order `json:"-"` Price int `json:"price"` Amount int `json:"amount"` } @@ -42,6 +40,11 @@ type OrderGetResponse struct { Transaction *Transaction `json:"transaction"` } +type OrderPurchaseRequest struct { + Purchase []OrderPurchase `json:"purchase"` + OrderID uint `json:"order"` +} + func (a *api) refundOrders() { const refundSleeptime = 10 * time.Minute for { @@ -55,7 +58,7 @@ func (a *api) deactivateOrders() { now := time.Now() t := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) err := a.db.Where("active = ? AND deadline < ?", true, t). - Preload("Purchases"). + Preload("Transactions.OrderPurchase"). Find(&orders).Error if err != nil { log.Println("Error refunding orders:", err) @@ -64,8 +67,10 @@ func (a *api) deactivateOrders() { for _, order := range orders { total := 0 - for _, purchase := range order.Purchases { - total += purchase.Price * purchase.Amount + for _, transaction := range order.Transactions { + for _, purchase := range transaction.OrderPurchase { + total += purchase.Price * purchase.Amount + } } transaction := Transaction{ @@ -105,7 +110,8 @@ func (a *api) ListActiveOrders(w http.ResponseWriter, req *http.Request) { func (a *api) listOrders(active bool, w http.ResponseWriter, req *http.Request) { var orders []Order - query := a.db.Preload(clause.Associations) + query := a.db.Preload(clause.Associations). + Preload("Transactions.OrderPurchase") if active { query = query.Where("active = ?", true) } @@ -129,6 +135,8 @@ func (a *api) GetOrder(num int, w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) var order Order err := a.db.Preload(clause.Associations). + Preload("Transactions.OrderPurchase"). + Preload("Transactions.Member"). First(&order, vars["id"]).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -143,10 +151,8 @@ func (a *api) GetOrder(num int, w http.ResponseWriter, req *http.Request) { body.Order = order var transaction Transaction - err = a.db.Where("member = ? AND type = 'order' AND id IN (?)", num, - a.db.Table("order_purchases"). - Where("order_id = ?", order.ID). - Select("transaction_id")). + err = a.db.Where("member = ? AND type = 'order' AND order_id = ?", num, vars["id"]). + Preload("OrderPurchase.Product"). Find(&transaction).Error if err != nil { log.Printf("Can't get order transaction %s: %v", vars["id"], err) @@ -196,14 +202,14 @@ func (a *api) AddOrder(num int, w http.ResponseWriter, req *http.Request) { } func (a *api) AddOrderPurchase(num int, w http.ResponseWriter, req *http.Request) { - var purchase []OrderPurchase - err := json.NewDecoder(req.Body).Decode(&purchase) + var request OrderPurchaseRequest + err := json.NewDecoder(req.Body).Decode(&request) if err != nil { - log.Printf("Can't parse order: %v", err) + log.Printf("Can't parse order purchase: %v", err) w.WriteHeader(http.StatusInternalServerError) return } - if len(purchase) == 0 { + if len(request.Purchase) == 0 { log.Printf("Empty order purchase") w.WriteHeader(http.StatusBadRequest) return @@ -211,43 +217,52 @@ func (a *api) AddOrderPurchase(num int, w http.ResponseWriter, req *http.Request var order Order err = a.db.Preload("Products"). - First(&order, purchase[0].OrderID).Error + Preload("Transactions"). + First(&order, request.OrderID).Error if err != nil { - log.Printf("Can't get order %d: %v", purchase[0].OrderID, err) + log.Printf("Can't get order %d: %v", request.OrderID, err) w.WriteHeader(http.StatusInternalServerError) return } if !order.Active { - log.Printf("Order is not active %d: %v", order.ID, purchase) + log.Printf("Order is not active %d: %v", order.ID, request) w.WriteHeader(http.StatusBadRequest) return } + for _, t := range order.Transactions { + if t.MemberNum == num { + log.Printf("Purchase by %d for %d when there is already one by this member: %v", num, order.ID, request) + w.WriteHeader(http.StatusBadRequest) + return + } + } total := 0 - for i, p := range purchase { + for i, p := range request.Purchase { found := false for _, product := range order.Products { if product.Code == p.ProductCode { total += product.Price * p.Amount - purchase[i].Price = product.Price + request.Purchase[i].Price = product.Price found = true break } } if !found { - log.Printf("Order purchase product %d not in order: %v", p.ProductCode, purchase) + log.Printf("Order purchase product %d not in order: %v", p.ProductCode, request) w.WriteHeader(http.StatusBadRequest) return } } transaction := Transaction{ - MemberNum: num, - Total: -total, - Type: "order", - Date: time.Now(), - Order: purchase, + MemberNum: num, + Total: -total, + Type: "order", + Date: time.Now(), + OrderPurchase: request.Purchase, + OrderID: &order.ID, } httpStatus, err := createTransaction(a.db, &transaction) if err != nil { diff --git a/api/order_test.go b/api/order_test.go index 0343838..a73261c 100644 --- a/api/order_test.go +++ b/api/order_test.go @@ -75,11 +75,13 @@ func TestOrderPurchase(t *testing.T) { t.Fatal("Can't get orders:", resp.Status) } - purchase := []OrderPurchase{ - { - ProductCode: testProduct.Code, - Amount: 3, - OrderID: orders[0].ID, + purchase := OrderPurchaseRequest{ + OrderID: orders[0].ID, + Purchase: []OrderPurchase{ + { + ProductCode: testProduct.Code, + Amount: 3, + }, }, } resp = tapi.do("POST", "/order/purchase", purchase, nil) @@ -170,11 +172,13 @@ func TestOrderDeactivation(t *testing.T) { t.Fatal("Didn't find my new order") } - purchase := []OrderPurchase{ - { - ProductCode: testProduct.Code, - Amount: 3, - OrderID: orders[0].ID, + purchase := OrderPurchaseRequest{ + OrderID: orders[0].ID, + Purchase: []OrderPurchase{ + { + ProductCode: testProduct.Code, + Amount: 3, + }, }, } resp = tapi.doAdmin("POST", "/order/purchase", purchase, nil) @@ -253,11 +257,13 @@ func TestGetOrder(t *testing.T) { t.Error("Wrong name:", body.Order.Name) } - purchase := []OrderPurchase{ - { - ProductCode: testProduct.Code, - Amount: 3, - OrderID: orders[0].ID, + purchase := OrderPurchaseRequest{ + OrderID: orders[0].ID, + Purchase: []OrderPurchase{ + { + ProductCode: testProduct.Code, + Amount: 3, + }, }, } resp = tapi.do("POST", "/order/purchase", purchase, nil) diff --git a/api/transaction.go b/api/transaction.go index f1bd209..6addd9d 100644 --- a/api/transaction.go +++ b/api/transaction.go @@ -22,16 +22,17 @@ type Transaction struct { Total int `json:"total"` Type string `json:"type"` - Purchase []Purchase `json:"purchase"` - Topup *Topup `json:"topup"` - Order []OrderPurchase `json:"order"` - Refund *Order `json:"refund"` + Purchase []Purchase `json:"purchase,omitempty"` + Topup *Topup `json:"topup,omitempty"` + OrderPurchase []OrderPurchase `json:"order_purchase,omitempty" gorm:"foreignKey:TransactionID"` + Order *Order `json:"order,omitempty"` + OrderID *uint `json:"-"` + Refund *Order `json:"refund,omitempty" gorm:"foreignKey:TransactionID"` } func (a *api) ListTransactions(w http.ResponseWriter, req *http.Request) { var transactions []Transaction - err := a.db.Preload("Purchase.Product"). - Preload(clause.Associations). + err := a.transactionQuery(). Order("date desc"). Find(&transactions).Error if err != nil { @@ -51,8 +52,7 @@ func (a *api) ListTransactions(w http.ResponseWriter, req *http.Request) { func (a *api) GetTransaction(num int, role string, w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) var transaction Transaction - err := a.db.Preload("Purchase.Product"). - Preload(clause.Associations). + err := a.transactionQuery(). First(&transaction, vars["id"]).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -87,9 +87,8 @@ func (a *api) GetMemberTransactions(w http.ResponseWriter, req *http.Request) { 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). + err := a.transactionQuery(). + Where("member = ?", num). Order("date desc"). Find(&transactions).Error if err != nil { @@ -136,3 +135,10 @@ func createTransaction(db *gorm.DB, transaction *Transaction) (httpStatus int, e }) return } + +func (a *api) transactionQuery() *gorm.DB { + return a.db.Preload("Purchase.Product"). + Preload("Order.Products"). + Preload("OrderPurchase.Product"). + Preload(clause.Associations) +} -- GitLab