diff --git a/api/api.go b/api/api.go index d4ea1737b52b2fd47c883670651d2961b323dfbb..3801c1314fd490de40fa973c148417282e289455 100644 --- a/api/api.go +++ b/api/api.go @@ -57,6 +57,7 @@ func Init(dbPath string, signKey string, mail *Mail, r *mux.Router) error { r.HandleFunc("/order", a.auth(a.ListOrders)).Methods("GET") r.HandleFunc("/order", a.authNum(a.AddOrder)).Methods("POST") r.HandleFunc("/order/{id:[0-9]+}", a.authNum(a.GetOrder)).Methods("GET") + r.HandleFunc("/order/{id:[0-9]+}", a.authNumRole(a.DeleteOrder)).Methods("DELETE") r.HandleFunc("/order/active", a.auth(a.ListActiveOrders)).Methods("GET") r.HandleFunc("/order/{id:[0-9]+}/purchase", a.authNum(a.AddOrderPurchase)).Methods("POST") return nil diff --git a/api/db/order.go b/api/db/order.go index 3a4cc7e3b00eb4b8b33d4df75170cf7781d4e485..e00e4b8cb70deb99fe8dd4d099399f1b0033dd79 100644 --- a/api/db/order.go +++ b/api/db/order.go @@ -48,8 +48,11 @@ func (d *DB) GetOrder(memberNum int, id int) (order Order, transaction Transacti Preload("Transactions.OrderPurchase"). Preload("Transactions.Member"). First(&order, id).Error - if errors.Is(err, gorm.ErrRecordNotFound) { - err = ErrorNotFound + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + err = ErrorNotFound + return + } return } err = d.db.Where("member = ? AND type = 'order' AND order_id = ?", memberNum, id). @@ -62,6 +65,48 @@ func (d *DB) AddOrder(order *Order) error { return d.db.Create(&order).Error } +func (d *DB) DeleteOrder(memberNum int, id int) error { + var order Order + err := d.db.Preload(clause.Associations). + Preload("Transactions.OrderPurchase"). + Preload("Transactions.Member"). + First(&order, id).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return ErrorNotFound + } + return err + } + if memberNum != 0 && order.MemberNum != memberNum { + return ErrorInvalidRequest + } + + return d.db.Transaction(func(tx *gorm.DB) error { + if order.TransactionID != nil { + var transaction Transaction + err = tx.First(&transaction, order.TransactionID).Error + if err != nil { + return err + } + order.Transactions = append(order.Transactions, transaction) + } + + for _, transaction := range order.Transactions { + err := updateMemberBalance(tx, transaction.MemberNum, -transaction.Total) + if err != nil { + return err + } + + err = tx.Select("OrderPurchase").Delete(&transaction).Error + if err != nil { + return err + } + } + + return tx.Delete(&order).Error + }) +} + func (d *DB) AddOrderPurchase(memberNum int, orderID int, purchase []OrderPurchase) (transaction Transaction, err error) { order, transaction, err := d.GetOrder(memberNum, orderID) if err != nil { diff --git a/api/db/transaction.go b/api/db/transaction.go index f51fad706f25cfb9c7a14a51b8804ce5015244da..151c3df37c175178cfa9ec2381f70cf97b62f673 100644 --- a/api/db/transaction.go +++ b/api/db/transaction.go @@ -20,8 +20,8 @@ type Transaction struct { Purchase []Purchase `json:"purchase,omitempty"` Topup *Topup `json:"topup,omitempty"` - OrderPurchase []OrderPurchase `json:"order_purchase,omitempty" gorm:"foreignKey:TransactionID"` - Order *Order `json:"order,omitempty"` + OrderPurchase []OrderPurchase `json:"order_purchase,omitempty" gorm:"foreignKey:TransactionID;constraint:OnDelete:CASCADE"` + Order *Order `json:"order,omitempty" gorm:"constraint:OnDelete:CASCADE"` OrderID *uint `json:"-"` Refund *Order `json:"refund,omitempty" gorm:"foreignKey:TransactionID"` } diff --git a/api/order.go b/api/order.go index 36fea279a48b60b36eaa077f770646a04750b95d..1945e24693315b5d5c6a45529da42b933f221c73 100644 --- a/api/order.go +++ b/api/order.go @@ -86,6 +86,29 @@ func (a *api) GetOrder(num int, w http.ResponseWriter, req *http.Request) { } } +func (a *api) DeleteOrder(num int, role string, w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + id, _ := strconv.Atoi(vars["id"]) + if role == "admin" { + num = 0 + } + err := a.db.DeleteOrder(num, id) + if err != nil { + if errors.Is(err, db.ErrorNotFound) { + w.WriteHeader(http.StatusNotFound) + return + } + if errors.Is(err, db.ErrorInvalidRequest) { + w.WriteHeader(http.StatusUnauthorized) + return + } + log.Printf("Can't get order %s: %v", vars["id"], err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) +} + func (a *api) AddOrder(num int, w http.ResponseWriter, req *http.Request) { var order db.Order err := json.NewDecoder(req.Body).Decode(&order) diff --git a/api/order_test.go b/api/order_test.go index f78827f2d5ae47f91dd99bb34edae4bbfcf8de27..f8ceb39652764d88ad13da67c568e64f860a61d9 100644 --- a/api/order_test.go +++ b/api/order_test.go @@ -66,6 +66,85 @@ func TestOrderActive(t *testing.T) { } } +func TestOrderDelete(t *testing.T) { + tapi := newTestAPI(t) + defer tapi.close() + tapi.addTestMember() + + order := testOrder + order.Deadline = time.Now().Add(-24 * time.Hour) + resp := tapi.do("POST", "/order", order, nil) + if resp.StatusCode != http.StatusCreated { + t.Fatal("Can't create order:", resp.Status) + } + + var orders []db.Order + resp = tapi.do("GET", "/order", nil, &orders) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get orders:", resp.Status) + } + + purchase := []db.OrderPurchase{ + { + ProductCode: testProduct.Code, + Amount: 3, + }, + } + resp = tapi.doAdmin("POST", fmt.Sprintf("/order/%d/purchase", orders[0].ID), purchase, nil) + if resp.StatusCode != http.StatusCreated { + t.Fatal("Can't create order purchase:", resp.Status) + } + + dbPath := path.Join(tapi.testPath, "test.db") + database, err := db.Init(dbPath) + if err != nil { + t.Fatal("Can't initialize the db:", err) + } + orders = database.DeactivateOrders() + if len(orders) != 1 { + t.Error("Deactivated wrong orders:", orders) + } + + resp = tapi.do("DELETE", fmt.Sprintf("/order/%d", orders[0].ID), nil, nil) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't delete order:", resp.Status) + } + + resp = tapi.do("GET", "/order", nil, &orders) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get orders:", resp.Status) + } + if len(orders) != 0 { + t.Error("The order didn't get removed", orders) + } + + var transactions []db.Transaction + resp = tapi.doAdmin("GET", "/transaction", nil, &transactions) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get orders:", resp.Status) + } + if len(transactions) != 0 { + t.Error("The transactions didn't get removed", transactions) + } + + var member db.Member + resp = tapi.doAdmin("GET", fmt.Sprintf("/member/%d", testMember.Num), nil, &member) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get orders:", resp.Status) + } + if member.Balance != testMember.Balance { + t.Error("Wrong balance on test member:", member.Balance) + } + + resp = tapi.doAdmin("GET", fmt.Sprintf("/member/%d", testMemberAdmin.Num), nil, &member) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get orders:", resp.Status) + } + if member.Balance != testMemberAdmin.Balance { + t.Error("Wrong balance on admin member:", member.Balance) + } +} + func TestOrderPurchase(t *testing.T) { tapi := newTestAPI(t) defer tapi.close()