diff --git a/api/api.go b/api/api.go index 57df5776f5a7138a86eaed19e70ff23caea66624..d4ea1737b52b2fd47c883670651d2961b323dfbb 100644 --- a/api/api.go +++ b/api/api.go @@ -58,7 +58,6 @@ func Init(dbPath string, signKey string, mail *Mail, r *mux.Router) error { r.HandleFunc("/order", a.authNum(a.AddOrder)).Methods("POST") r.HandleFunc("/order/{id:[0-9]+}", a.authNum(a.GetOrder)).Methods("GET") r.HandleFunc("/order/active", a.auth(a.ListActiveOrders)).Methods("GET") - r.HandleFunc("/order/purchase", a.authNum(a.AddOrderPurchase)).Methods("POST") - // TODO: r.HandleFunc("/order/purchase", a.authNum(a.UpdateOrderPurchase)).Methods("PUT") + 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 d1e3335a734de74ab6d3a4a7fe672fab7402a887..3a4cc7e3b00eb4b8b33d4df75170cf7781d4e485 100644 --- a/api/db/order.go +++ b/api/db/order.go @@ -62,11 +62,8 @@ func (d *DB) AddOrder(order *Order) error { return d.db.Create(&order).Error } -func (d *DB) AddOrderPurchase(memberNum int, orderID uint, purchase []OrderPurchase) (transaction Transaction, err error) { - var order Order - err = d.db.Preload("Products"). - Preload("Transactions"). - First(&order, orderID).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 { return } @@ -75,13 +72,6 @@ func (d *DB) AddOrderPurchase(memberNum int, orderID uint, purchase []OrderPurch log.Printf("Order is not active %d: %v", order.ID, purchase) return } - for _, t := range order.Transactions { - if t.MemberNum == memberNum { - log.Printf("Purchase by %d for %d when there is already one by this member: %v", memberNum, order.ID, purchase) - err = ErrorInvalidRequest - return - } - } total := 0 for i, p := range purchase { @@ -102,6 +92,11 @@ func (d *DB) AddOrderPurchase(memberNum int, orderID uint, purchase []OrderPurch } } + if transaction.ID != 0 { + err = d.updateOrderPurchase(memberNum, &transaction, total, purchase) + return + } + transaction = Transaction{ MemberNum: memberNum, Total: -total, @@ -114,6 +109,29 @@ func (d *DB) AddOrderPurchase(memberNum int, orderID uint, purchase []OrderPurch return } +func (d *DB) updateOrderPurchase(memberNum int, transaction *Transaction, total int, purchase []OrderPurchase) error { + totalDiff := -(transaction.Total + total) + transaction.Total = -total + transaction.Date = time.Now() + + for i, p := range transaction.OrderPurchase { + for _, new_purchase := range purchase { + if new_purchase.ProductCode == p.ProductCode { + transaction.OrderPurchase[i].Amount = new_purchase.Amount + break + } + } + } + + return d.db.Transaction(func(tx *gorm.DB) error { + err := updateMemberBalance(tx, memberNum, totalDiff) + if err != nil { + return err + } + return tx.Save(&transaction).Error + }) +} + func (d *DB) DeactivateOrders() []Order { var orders []Order now := time.Now() diff --git a/api/db/transaction.go b/api/db/transaction.go index 408956e154f726ef1def0e74264a4d7170a16535..f51fad706f25cfb9c7a14a51b8804ce5015244da 100644 --- a/api/db/transaction.go +++ b/api/db/transaction.go @@ -137,24 +137,30 @@ func (d *DB) transactionQuery() *gorm.DB { func createTransaction(db *gorm.DB, transaction *Transaction) error { return db.Transaction(func(tx *gorm.DB) error { - var member Member - err := tx.Where("num = ?", transaction.MemberNum).Find(&member).Error + err := updateMemberBalance(tx, transaction.MemberNum, transaction.Total) if err != nil { - log.Printf("Can't find member for transaction %d: %v", transaction.MemberNum, err) - return ErrorNotFound - } - if member.Balance < -transaction.Total { - log.Printf("Member %d don't have enough money (%d-%d)", member.Num, member.Balance, transaction.Total) - return ErrorInvalidRequest - } - err = tx.Model(&Member{}). - Where("num = ?", transaction.MemberNum). - Update("balance", gorm.Expr("balance + ?", transaction.Total)).Error - if err != nil { - log.Printf("Can't update update member balance %d-%d: %v", member.Num, transaction.Total, err) return err } - return tx.Create(&transaction).Error }) } + +func updateMemberBalance(tx *gorm.DB, memberNum int, amount int) error { + var member Member + err := tx.Where("num = ?", memberNum).Find(&member).Error + if err != nil { + log.Printf("Can't find member for transaction %d: %v", memberNum, err) + return ErrorNotFound + } + if member.Balance < -amount { + log.Printf("Member %d don't have enough money (%d-%d)", member.Num, member.Balance, amount) + return ErrorInvalidRequest + } + err = tx.Model(&Member{}). + Where("num = ?", memberNum). + Update("balance", gorm.Expr("balance + ?", amount)).Error + if err != nil { + log.Printf("Can't update update member balance %d-%d: %v", member.Num, amount, err) + } + return err +} diff --git a/api/order.go b/api/order.go index a296f50de53de3c1bcd17fc39e567f7d719be32d..36fea279a48b60b36eaa077f770646a04750b95d 100644 --- a/api/order.go +++ b/api/order.go @@ -17,11 +17,6 @@ type OrderGetResponse struct { Transaction *db.Transaction `json:"transaction"` } -type OrderPurchaseRequest struct { - Purchase []db.OrderPurchase `json:"purchase"` - OrderID uint `json:"order"` -} - func (a *api) refundOrders() { const refundSleeptime = 10 * time.Minute for { @@ -120,27 +115,29 @@ 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 request OrderPurchaseRequest - err := json.NewDecoder(req.Body).Decode(&request) + vars := mux.Vars(req) + id, _ := strconv.Atoi(vars["id"]) + var purchase []db.OrderPurchase + err := json.NewDecoder(req.Body).Decode(&purchase) if err != nil { log.Printf("Can't parse order purchase: %v", err) w.WriteHeader(http.StatusInternalServerError) return } - if len(request.Purchase) == 0 { + if len(purchase) == 0 { log.Printf("Empty order purchase") w.WriteHeader(http.StatusBadRequest) return } - transaction, err := a.db.AddOrderPurchase(num, request.OrderID, request.Purchase) + transaction, err := a.db.AddOrderPurchase(num, id, purchase) if err != nil { if errors.Is(err, db.ErrorNotFound) { w.WriteHeader(http.StatusNotAcceptable) } else if errors.Is(err, db.ErrorInvalidRequest) { w.WriteHeader(http.StatusBadRequest) } else { - log.Printf("Can't get order %d: %v", request.OrderID, err) + log.Printf("Can't get order %d: %v", id, err) w.WriteHeader(http.StatusInternalServerError) } return @@ -152,6 +149,5 @@ func (a *api) AddOrderPurchase(num int, w http.ResponseWriter, req *http.Request if err != nil { log.Printf("Can't encode order transaction: %v", err) w.WriteHeader(http.StatusInternalServerError) - return } } diff --git a/api/order_test.go b/api/order_test.go index af3626159820666a5eacc1d2f6206458bd6b5891..f78827f2d5ae47f91dd99bb34edae4bbfcf8de27 100644 --- a/api/order_test.go +++ b/api/order_test.go @@ -1,6 +1,7 @@ package api import ( + "fmt" "net/http" "path" "strconv" @@ -77,16 +78,13 @@ func TestOrderPurchase(t *testing.T) { t.Fatal("Can't get orders:", resp.Status) } - purchase := OrderPurchaseRequest{ - OrderID: orders[0].ID, - Purchase: []db.OrderPurchase{ - { - ProductCode: testProduct.Code, - Amount: 3, - }, + purchase := []db.OrderPurchase{ + { + ProductCode: testProduct.Code, + Amount: 3, }, } - resp = tapi.do("POST", "/order/purchase", purchase, nil) + resp = tapi.do("POST", fmt.Sprintf("/order/%d/purchase", orders[0].ID), purchase, nil) if resp.StatusCode != http.StatusCreated { t.Fatal("Can't create order:", resp.Status) } @@ -176,16 +174,13 @@ func TestOrderDeactivation(t *testing.T) { t.Fatal("Didn't find my new order") } - purchase := OrderPurchaseRequest{ - OrderID: orders[0].ID, - Purchase: []db.OrderPurchase{ - { - ProductCode: testProduct.Code, - Amount: 3, - }, + purchase := []db.OrderPurchase{ + { + ProductCode: testProduct.Code, + Amount: 3, }, } - resp = tapi.doAdmin("POST", "/order/purchase", purchase, nil) + 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:", resp.Status) } @@ -263,16 +258,13 @@ func TestGetOrder(t *testing.T) { t.Error("Wrong name:", body.Order.Name) } - purchase := OrderPurchaseRequest{ - OrderID: orders[0].ID, - Purchase: []db.OrderPurchase{ - { - ProductCode: testProduct.Code, - Amount: 3, - }, + purchase := []db.OrderPurchase{ + { + ProductCode: testProduct.Code, + Amount: 3, }, } - resp = tapi.do("POST", "/order/purchase", purchase, nil) + resp = tapi.do("POST", fmt.Sprintf("/order/%d/purchase", orders[0].ID), purchase, nil) if resp.StatusCode != http.StatusCreated { t.Fatal("Can't create order:", resp.Status) } @@ -289,6 +281,66 @@ func TestGetOrder(t *testing.T) { } } +func TestUpdateOrderPurchase(t *testing.T) { + tapi := newTestAPI(t) + defer tapi.close() + tapi.addTestMember() + tapi.addTestOrder() + + var orders []db.Order + resp := tapi.do("GET", "/order/active", 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.do("POST", fmt.Sprintf("/order/%d/purchase", orders[0].ID), purchase, nil) + if resp.StatusCode != http.StatusCreated { + t.Fatal("Can't create order:", resp.Status) + } + + purchase[0].Amount = 2 + resp = tapi.do("POST", fmt.Sprintf("/order/%d/purchase", orders[0].ID), purchase, nil) + if resp.StatusCode != http.StatusCreated { + t.Fatal("Can't create order:", resp.Status) + } + + var transactions []db.Transaction + resp = tapi.do("GET", "/transaction/mine", nil, &transactions) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get transactions:", resp.Status) + } + if len(transactions) != 1 { + t.Fatal("Wrong number of transactions", len(orders), orders) + } + total := 2 * testProduct.Price + if transactions[0].Total != -total { + t.Fatal("Wrong total", transactions[0].Total) + } + + resp = tapi.do("GET", "/order/active", nil, &orders) + if resp.StatusCode != http.StatusOK { + t.Fatal("Can't get orders:", resp.Status) + } + if len(orders[0].Transactions[0].OrderPurchase) != 1 { + t.Fatal("Wrong number of product purchases:", orders[0].Transactions[0].OrderPurchase) + } + + var member db.Member + resp = tapi.do("GET", "/member/me", nil, &member) + if resp.StatusCode != http.StatusOK { + t.Error("Can't find the member:", resp.Status) + } + if member.Balance != testMember.Balance-total { + t.Error("Wrong product balance:", member.Balance) + } +} + func (tapi *testAPI) addTestOrder() { resp := tapi.do("POST", "/order", testOrder, nil) if resp.StatusCode != http.StatusCreated { diff --git a/src/order/PurchaseOrder.js b/src/order/PurchaseOrder.js index 3adb7ae707c26100c56b37338f401591c43adda2..0f0ccef5e55187bd1b85479ac3bc9e44a516f4ce 100644 --- a/src/order/PurchaseOrder.js +++ b/src/order/PurchaseOrder.js @@ -49,12 +49,9 @@ class PurchaseOrder extends React.Component { amount: parseInt(p.amount), }; }); - const body = JSON.stringify({ - order: this.props.order.ID, - purchase, - }); + const body = JSON.stringify(purchase); console.log(body); - fetch("/api/order/purchase", { + fetch("/api/order/" + this.props.order.ID + "purchase", { headers: { "x-authentication": this.context.token }, method: "POST", body,