Skip to content
Snippets Groups Projects
order.go 7.27 KiB
Newer Older
  • Learn to ignore specific revisions
  • meskio's avatar
    meskio committed
    package api
    
    import (
    	"encoding/json"
    
    meskio's avatar
    meskio committed
    	"log"
    	"net/http"
    	"time"
    
    	"github.com/gorilla/mux"
    	"gorm.io/gorm"
    	"gorm.io/gorm/clause"
    )
    
    type Order struct {
    	gorm.Model
    	Name        string    `json:"name"`
    	Description string    `json:"description"`
    
    meskio's avatar
    meskio committed
    	MemberNum   int       `json:"member_num" gorm:"column:member"`
    
    meskio's avatar
    meskio committed
    	Member      *Member   `json:"member,omitempty" gorm:"foreignKey:MemberNum;references:Num"`
    	Deadline    time.Time `json:"deadline"`
    	Active      bool      `json:"active" gorm:"index"`
    
    
    meskio's avatar
    meskio committed
    	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"`
    
    meskio's avatar
    meskio committed
    }
    
    type OrderPurchase struct {
    
    	gorm.Model    `json:"-"`
    	TransactionID uint     `json:"-"`
    	ProductCode   int      `json:"product_code"`
    	Product       *Product `json:"product" gorm:"foreignKey:ProductCode;references:Code"`
    	Price         int      `json:"price"`
    
    Quique's avatar
    Quique committed
    	Amount        int      `json:"amount"`
    
    meskio's avatar
    meskio committed
    }
    
    
    meskio's avatar
    meskio committed
    type OrderGetResponse struct {
    	Order       Order        `json:"order"`
    	Transaction *Transaction `json:"transaction"`
    }
    
    
    meskio's avatar
    meskio committed
    type OrderPurchaseRequest struct {
    	Purchase []OrderPurchase `json:"purchase"`
    	OrderID  uint            `json:"order"`
    }
    
    
    meskio's avatar
    meskio committed
    func (a *api) refundOrders() {
    	const refundSleeptime = 10 * time.Minute
    	for {
    		time.Sleep(refundSleeptime)
    		a.deactivateOrders()
    	}
    }
    
    func (a *api) deactivateOrders() {
    	var orders []Order
    	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).
    
    meskio's avatar
    meskio committed
    		Preload("Transactions.OrderPurchase").
    
    meskio's avatar
    meskio committed
    		Find(&orders).Error
    	if err != nil {
    		log.Println("Error refunding orders:", err)
    		return
    	}
    
    	for _, order := range orders {
    		total := 0
    
    meskio's avatar
    meskio committed
    		for _, transaction := range order.Transactions {
    			for _, purchase := range transaction.OrderPurchase {
    				total += purchase.Price * purchase.Amount
    			}
    
    meskio's avatar
    meskio committed
    		}
    
    		transaction := Transaction{
    			MemberNum: order.MemberNum,
    			Date:      time.Now(),
    			Type:      "refund",
    			Total:     total,
    		}
    		err = a.db.Transaction(func(tx *gorm.DB) error {
    			_, err := createTransaction(tx, &transaction)
    			if err != nil {
    				return err
    			}
    			return tx.Model(&Order{}).
    				Where("id = ?", order.ID).
    				Updates(map[string]interface{}{
    					"active":      false,
    					"transaction": transaction.ID}).
    				Error
    		})
    		if err != nil {
    			log.Printf("Can't create refund: %v\n%v", err, order)
    			continue
    		}
    
    
    		log.Println("Refund order", order.Name, total)
    
    meskio's avatar
    meskio committed
    	}
    }
    
    func (a *api) ListOrders(w http.ResponseWriter, req *http.Request) {
    	a.listOrders(false, w, req)
    }
    
    func (a *api) ListActiveOrders(w http.ResponseWriter, req *http.Request) {
    	a.listOrders(true, w, req)
    }
    
    func (a *api) listOrders(active bool, w http.ResponseWriter, req *http.Request) {
    	var orders []Order
    
    meskio's avatar
    meskio committed
    	query := a.db.Preload(clause.Associations).
    		Preload("Transactions.OrderPurchase")
    
    meskio's avatar
    meskio committed
    	if active {
    		query = query.Where("active = ?", true)
    	}
    	err := query.Order("deadline desc").
    		Find(&orders).Error
    	if err != nil {
    		log.Printf("Can't list orders: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    	w.Header().Set("Content-Type", "application/json")
    	w.WriteHeader(http.StatusOK)
    	err = json.NewEncoder(w).Encode(orders)
    	if err != nil {
    		log.Printf("Can't encode orders: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    	}
    }
    
    
    func (a *api) GetOrder(num int, w http.ResponseWriter, req *http.Request) {
    
    meskio's avatar
    meskio committed
    	vars := mux.Vars(req)
    	var order Order
    
    	err := a.db.Preload(clause.Associations).
    
    meskio's avatar
    meskio committed
    		Preload("Transactions.OrderPurchase").
    		Preload("Transactions.Member").
    
    meskio's avatar
    meskio committed
    		First(&order, vars["id"]).Error
    	if err != nil {
    
    		if errors.Is(err, gorm.ErrRecordNotFound) {
    
    meskio's avatar
    meskio committed
    			w.WriteHeader(http.StatusNotFound)
    			return
    		}
    		log.Printf("Can't get order %s: %v", vars["id"], err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    
    meskio's avatar
    meskio committed
    	var body OrderGetResponse
    
    	body.Order = order
    
    	var transaction Transaction
    
    meskio's avatar
    meskio committed
    	err = a.db.Where("member = ? AND type = 'order' AND order_id = ?", num, vars["id"]).
    		Preload("OrderPurchase.Product").
    
    		Find(&transaction).Error
    	if err != nil {
    
    meskio's avatar
    meskio committed
    		log.Printf("Can't get order transaction %s: %v", vars["id"], err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    	if transaction.ID != 0 {
    
    		body.Transaction = &transaction
    	}
    
    meskio's avatar
    meskio committed
    
    	w.Header().Set("Content-Type", "application/json")
    	w.WriteHeader(http.StatusOK)
    
    	err = json.NewEncoder(w).Encode(body)
    
    meskio's avatar
    meskio committed
    	if err != nil {
    		log.Printf("Can't encode order: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    }
    
    func (a *api) AddOrder(num int, w http.ResponseWriter, req *http.Request) {
    	var order Order
    	err := json.NewDecoder(req.Body).Decode(&order)
    	if err != nil {
    		log.Printf("Can't parse order: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    
    	order.MemberNum = num
    	order.Active = true
    	err = a.db.Create(&order).Error
    	if err != nil {
    		log.Printf("Can't create order: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    
    	w.Header().Set("Content-Type", "application/json")
    	w.WriteHeader(http.StatusCreated)
    	err = json.NewEncoder(w).Encode(order)
    	if err != nil {
    		log.Printf("Can't encode order: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    }
    
    func (a *api) AddOrderPurchase(num int, w http.ResponseWriter, req *http.Request) {
    
    meskio's avatar
    meskio committed
    	var request OrderPurchaseRequest
    	err := json.NewDecoder(req.Body).Decode(&request)
    
    meskio's avatar
    meskio committed
    	if err != nil {
    
    meskio's avatar
    meskio committed
    		log.Printf("Can't parse order purchase: %v", err)
    
    meskio's avatar
    meskio committed
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    
    meskio's avatar
    meskio committed
    	if len(request.Purchase) == 0 {
    
    meskio's avatar
    meskio committed
    		log.Printf("Empty order purchase")
    		w.WriteHeader(http.StatusBadRequest)
    		return
    	}
    
    	var order Order
    
    	err = a.db.Preload("Products").
    
    meskio's avatar
    meskio committed
    		Preload("Transactions").
    		First(&order, request.OrderID).Error
    
    meskio's avatar
    meskio committed
    	if err != nil {
    
    meskio's avatar
    meskio committed
    		log.Printf("Can't get order %d: %v", request.OrderID, err)
    
    		w.WriteHeader(http.StatusInternalServerError)
    
    meskio's avatar
    meskio committed
    		return
    	}
    	if !order.Active {
    
    meskio's avatar
    meskio committed
    		log.Printf("Order is not active %d: %v", order.ID, request)
    
    meskio's avatar
    meskio committed
    		w.WriteHeader(http.StatusBadRequest)
    		return
    	}
    
    meskio's avatar
    meskio committed
    	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
    		}
    	}
    
    meskio's avatar
    meskio committed
    
    	total := 0
    
    meskio's avatar
    meskio committed
    	for i, p := range request.Purchase {
    
    meskio's avatar
    meskio committed
    		found := false
    		for _, product := range order.Products {
    
    			if product.Code == p.ProductCode {
    
    Quique's avatar
    Quique committed
    				total += product.Price * p.Amount
    
    meskio's avatar
    meskio committed
    				request.Purchase[i].Price = product.Price
    
    meskio's avatar
    meskio committed
    				found = true
    				break
    			}
    		}
    
    		if !found {
    
    meskio's avatar
    meskio committed
    			log.Printf("Order purchase product %d not in order: %v", p.ProductCode, request)
    
    			w.WriteHeader(http.StatusBadRequest)
    
    meskio's avatar
    meskio committed
    			return
    		}
    	}
    
    	transaction := Transaction{
    
    meskio's avatar
    meskio committed
    		MemberNum:     num,
    		Total:         -total,
    		Type:          "order",
    		Date:          time.Now(),
    		OrderPurchase: request.Purchase,
    		OrderID:       &order.ID,
    
    meskio's avatar
    meskio committed
    	}
    	httpStatus, err := createTransaction(a.db, &transaction)
    	if err != nil {
    		log.Println(err)
    		w.WriteHeader(httpStatus)
    		return
    	}
    
    	w.Header().Set("Content-Type", "application/json")
    	w.WriteHeader(http.StatusCreated)
    	err = json.NewEncoder(w).Encode(transaction)
    	if err != nil {
    		log.Printf("Can't encode order transaction: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    }