Skip to content
Snippets Groups Projects
order.go 6.72 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"`
    	MemberNum   int       `json:"-" 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"`
    
    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"`
    	OrderID       uint     `json:"order_id"`
    	Order         *Order   `json:"-"`
    	Price         int      `json:"price"`
    	Ammount       int      `json:"ammount"`
    
    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
    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).
    
    		Preload("Purchases").
    
    meskio's avatar
    meskio committed
    		Find(&orders).Error
    	if err != nil {
    		log.Println("Error refunding orders:", err)
    		return
    	}
    
    	for _, order := range orders {
    		total := 0
    
    		for _, purchase := range order.Purchases {
    			total += purchase.Price * purchase.Ammount
    
    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
    
    	query := a.db.Preload(clause.Associations)
    
    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
    		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 id IN (?)", num,
    		a.db.Table("order_purchases").
    			Where("order_id = ?", order.ID).
    			Select("transaction_id")).
    
    		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) {
    	var purchase []OrderPurchase
    	err := json.NewDecoder(req.Body).Decode(&purchase)
    	if err != nil {
    		log.Printf("Can't parse order: %v", err)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    	if len(purchase) == 0 {
    		log.Printf("Empty order purchase")
    		w.WriteHeader(http.StatusBadRequest)
    		return
    	}
    
    	var order Order
    
    	err = a.db.Preload("Products").
    		First(&order, purchase[0].OrderID).Error
    
    meskio's avatar
    meskio committed
    	if err != nil {
    
    		log.Printf("Can't get order %d: %v", purchase[0].OrderID, err)
    		w.WriteHeader(http.StatusInternalServerError)
    
    meskio's avatar
    meskio committed
    		return
    	}
    	if !order.Active {
    		log.Printf("Order is not active %d: %v", order.ID, purchase)
    		w.WriteHeader(http.StatusBadRequest)
    		return
    	}
    
    	total := 0
    
    	for i, p := range purchase {
    
    meskio's avatar
    meskio committed
    		found := false
    		for _, product := range order.Products {
    
    			if product.Code == p.ProductCode {
    
    meskio's avatar
    meskio committed
    				total += product.Price * p.Ammount
    
    				purchase[i].Price = product.Price
    
    meskio's avatar
    meskio committed
    				found = true
    				break
    			}
    		}
    
    		if !found {
    
    			log.Printf("Order purchase product %d not in order: %v", p.ProductCode, purchase)
    			w.WriteHeader(http.StatusBadRequest)
    
    meskio's avatar
    meskio committed
    			return
    		}
    	}
    
    	transaction := Transaction{
    		MemberNum: num,
    		Total:     -total,
    		Type:      "order",
    		Date:      time.Now(),
    		Order:     purchase,
    	}
    	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
    	}
    }